// AS4 Instruction Intake Service
// Validates business schema, deduplication, idempotency

import { v4 as uuidv4 } from 'uuid';
import prisma from '@/shared/database/prisma';
import { messageValidatorService } from './messages/message-validator.service';
import { messageTransformerService, InternalInstruction } from './messages/message-transformer.service';
import { as4SecurityService } from '../as4/as4-security.service';
import { as4PayloadVaultService } from '../as4/as4-payload-vault.service';
import { logger } from '@/infrastructure/monitoring/logger';
import { Decimal } from '@prisma/client/runtime/library';

export interface InstructionIntakeResult {
  instructionId: string;
  status: 'ACCEPTED' | 'REJECTED';
  reason?: string;
  existingInstructionId?: string;
}

export class InstructionIntakeService {
  /**
   * Process instruction intake
   */
  async processInstruction(
    as4Message: any,
    fromMemberId: string,
    payloadHash: string,
    signatureEvidence?: any,
    as4ReceiptEvidence?: any
  ): Promise<InstructionIntakeResult> {
    try {
      // Step 1: Validate message schema
      const validation = messageValidatorService.validateInstruction(as4Message);
      if (!validation.valid) {
        return {
          instructionId: as4Message.Instr?.InstrId || 'UNKNOWN',
          status: 'REJECTED',
          reason: `Schema validation failed: ${validation.errors?.join(', ')}`,
        };
      }

      // Step 2: Transform to internal format
      const internal = messageTransformerService.transformInstructionToInternal(as4Message);

      // Step 3: Check idempotency (InstrId + FromMemberId)
      const existing = await prisma.as4SettlementInstruction.findUnique({
        where: {
          fromMemberId_instructionId: {
            fromMemberId: internal.fromMemberId,
            instructionId: internal.instructionId,
          },
        },
      });

      if (existing) {
        logger.info('Duplicate instruction detected', {
          instructionId: internal.instructionId,
          fromMemberId: internal.fromMemberId,
          existingStatus: existing.status,
        });

        return {
          instructionId: internal.instructionId,
          status: existing.status === 'REJECTED' ? 'REJECTED' : 'ACCEPTED',
          reason: existing.status === 'REJECTED' ? existing.rejectReason || 'Previously rejected' : 'Duplicate instruction',
          existingInstructionId: existing.id,
        };
      }

      // Step 4: Store instruction
      const instruction = await prisma.as4SettlementInstruction.create({
        data: {
          id: uuidv4(),
          instructionId: internal.instructionId,
          messageId: internal.messageId,
          fromMemberId: internal.fromMemberId,
          toMemberId: internal.toMemberId,
          correlationId: internal.correlationId,
          businessType: internal.businessType,
          valueDate: internal.valueDate,
          currency: internal.currency,
          amount: new Decimal(internal.amount),
          debtorAccount: internal.debtorAccount,
          creditorAccount: internal.creditorAccount,
          status: 'RECEIVED',
          payloadHash,
          signatureEvidence: signatureEvidence || null,
          as4ReceiptEvidence: as4ReceiptEvidence || null,
          compliancePackageRef: internal.compliancePackageRef || null,
        },
      });

      logger.info('Instruction received', {
        instructionId: internal.instructionId,
        fromMemberId: internal.fromMemberId,
        amount: internal.amount,
        currency: internal.currency,
      });

      return {
        instructionId: internal.instructionId,
        status: 'ACCEPTED',
      };
    } catch (error) {
      logger.error('Instruction intake failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
        messageId: as4Message.MessageId,
      });

      return {
        instructionId: as4Message.Instr?.InstrId || 'UNKNOWN',
        status: 'REJECTED',
        reason: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }

  /**
   * Update instruction status
   */
  async updateInstructionStatus(
    instructionId: string,
    fromMemberId: string,
    status: 'ACCEPTED' | 'QUEUED' | 'POSTED_FINAL' | 'REJECTED',
    reason?: string
  ): Promise<void> {
    await prisma.as4SettlementInstruction.updateMany({
      where: {
        instructionId,
        fromMemberId,
      },
      data: {
        status,
        rejectReason: reason || null,
        updatedAt: new Date(),
      },
    });
  }

  /**
   * Get instruction by ID
   */
  async getInstruction(instructionId: string, fromMemberId: string) {
    return prisma.as4SettlementInstruction.findUnique({
      where: {
        fromMemberId_instructionId: {
          instructionId,
          fromMemberId,
        },
      },
      include: {
        advices: true,
      },
    });
  }
}

export const instructionIntakeService = new InstructionIntakeService();
