// AS4 Settlement Orchestrator Service
// Orchestrates the complete settlement lifecycle

import { instructionIntakeService } from './instruction-intake.service';
import { liquidityLimitsService } from './liquidity-limits.service';
import { complianceGateService } from './compliance-gate.service';
import { postingEngineService } from './posting-engine.service';
import { adviceGeneratorService } from './advice-generator.service';
import { logger } from '@/infrastructure/monitoring/logger';

export class SettlementOrchestratorService {
  /**
   * Process settlement instruction end-to-end
   */
  async processSettlementInstruction(data: {
    as4Message: any;
    fromMemberId: string;
    payloadHash: string;
    signatureEvidence?: any;
    as4ReceiptEvidence?: any;
  }): Promise<{
    instructionId: string;
    status: string;
    postingId?: string;
    error?: string;
  }> {
    try {
      // Step 1: Instruction intake
      const intakeResult = await instructionIntakeService.processInstruction(
        data.as4Message,
        data.fromMemberId,
        data.payloadHash,
        data.signatureEvidence,
        data.as4ReceiptEvidence
      );

      if (intakeResult.status === 'REJECTED') {
        return {
          instructionId: intakeResult.instructionId,
          status: 'REJECTED',
          error: intakeResult.reason,
        };
      }

      const instructionId = intakeResult.instructionId;

      // Step 2: Business validation (already done in intake)
      await instructionIntakeService.updateInstructionStatus(
        instructionId,
        data.fromMemberId,
        'ACCEPTED'
      );

      // Step 3: Liquidity and limits check
      const liquidityResult = await liquidityLimitsService.checkLiquidity(
        data.fromMemberId,
        data.as4Message.Instr.DebtorAccount,
        data.as4Message.Instr.Amount,
        data.as4Message.Instr.Currency
      );

      if (!liquidityResult.sufficient) {
        await instructionIntakeService.updateInstructionStatus(
          instructionId,
          data.fromMemberId,
          'REJECTED',
          liquidityResult.reason
        );
        return {
          instructionId,
          status: 'REJECTED',
          error: liquidityResult.reason,
        };
      }

      const limitsResult = await liquidityLimitsService.checkLimits(
        data.fromMemberId,
        data.as4Message.Instr.Amount,
        data.as4Message.Instr.Currency
      );

      if (!limitsResult.withinLimits) {
        await instructionIntakeService.updateInstructionStatus(
          instructionId,
          data.fromMemberId,
          'REJECTED',
          limitsResult.reason
        );
        return {
          instructionId,
          status: 'REJECTED',
          error: limitsResult.reason,
        };
      }

      // Step 4: Compliance gate
      const complianceResult = await complianceGateService.checkCompliance({
        fromMemberId: data.fromMemberId,
        toMemberId: data.as4Message.Instr.CreditorAccount.split(':')[1],
        amount: data.as4Message.Instr.Amount,
        currency: data.as4Message.Instr.Currency,
        debtorAccount: data.as4Message.Instr.DebtorAccount,
        creditorAccount: data.as4Message.Instr.CreditorAccount,
        compliancePackageRef: data.as4Message.Instr.CompliancePackageRef,
      });

      if (!complianceResult.passed) {
        await instructionIntakeService.updateInstructionStatus(
          instructionId,
          data.fromMemberId,
          'REJECTED',
          complianceResult.reason
        );
        return {
          instructionId,
          status: 'REJECTED',
          error: complianceResult.reason,
        };
      }

      // Step 5: Posting
      await instructionIntakeService.updateInstructionStatus(
        instructionId,
        data.fromMemberId,
        'QUEUED'
      );

      const postingResult = await postingEngineService.postInstruction({
        instructionId,
        fromMemberId: data.fromMemberId,
        toMemberId: data.as4Message.Instr.CreditorAccount.split(':')[1],
        debtorAccount: data.as4Message.Instr.DebtorAccount,
        creditorAccount: data.as4Message.Instr.CreditorAccount,
        amount: data.as4Message.Instr.Amount,
        currency: data.as4Message.Instr.Currency,
        valueDate: new Date(data.as4Message.Instr.ValueDate),
      });

      if (postingResult.status === 'FAILED') {
        return {
          instructionId,
          status: 'REJECTED',
          error: postingResult.error,
        };
      }

      // Step 6: Generate and send advices
      await adviceGeneratorService.generateDebitAdvice({
        instructionId,
        postingId: postingResult.postingId!,
        debtorAccount: data.as4Message.Instr.DebtorAccount,
        amount: data.as4Message.Instr.Amount,
        currency: data.as4Message.Instr.Currency,
      });

      await adviceGeneratorService.generateCreditAdvice({
        instructionId,
        postingId: postingResult.postingId!,
        creditorAccount: data.as4Message.Instr.CreditorAccount,
        amount: data.as4Message.Instr.Amount,
        currency: data.as4Message.Instr.Currency,
      });

      logger.info('Settlement instruction processed successfully', {
        instructionId,
        postingId: postingResult.postingId,
      });

      return {
        instructionId,
        status: 'POSTED_FINAL',
        postingId: postingResult.postingId,
      };
    } catch (error) {
      logger.error('Settlement orchestration failed', {
        error: error instanceof Error ? error.message : 'Unknown error',
        fromMemberId: data.fromMemberId,
      });

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

export const settlementOrchestratorService = new SettlementOrchestratorService();
