// SWIFT Message Adapter
// Maps SWIFT MT/MX messages to DBIS Nostro/Vostro API

import { BasePluginAdapter } from './generic-adapter';
import {
  ParticipantCreateRequest,
  AccountCreateRequest,
  TransferCreateRequest,
  NostroVostroTransfer,
  NostroVostroAccount,
  AccountType,
} from '@/core/nostro-vostro/nostro-vostro.types';

/**
 * SWIFT MT103 (Customer Credit Transfer) structure
 */
interface SwiftMT103 {
  messageType: '103';
  senderBIC: string;
  receiverBIC: string;
  transactionReference: string;
  valueDate: string;
  currency: string;
  amount: string;
  orderingCustomer: {
    name: string;
    account?: string;
  };
  beneficiaryCustomer: {
    name: string;
    account?: string;
  };
  remittanceInfo?: string;
}

/**
 * SWIFT MT202 (General Financial Institution Transfer) structure
 */
interface SwiftMT202 {
  messageType: '202';
  senderBIC: string;
  receiverBIC: string;
  transactionReference: string;
  valueDate: string;
  currency: string;
  amount: string;
  senderAccount?: string;
  receiverAccount?: string;
  remittanceInfo?: string;
}

/**
 * SWIFT MX (ISO 20022) pacs.008 structure
 */
interface SwiftMX {
  messageType: 'pacs.008';
  messageId: string;
  creationDateTime: string;
  debtor: {
    bicfi?: string;
    account?: string;
  };
  creditor: {
    bicfi?: string;
    account?: string;
  };
  amount: {
    currency: string;
    amount: string;
  };
  valueDate: string;
  remittanceInfo?: string;
}

export class SwiftAdapter extends BasePluginAdapter {
  constructor(config: Record<string, unknown> = {}) {
    super('SWIFT', '1.0.0', config);
  }

  async isAvailable(): Promise<boolean> {
    // SWIFT adapter is always available (message parsing)
    return true;
  }

  /**
   * Parse SWIFT message string
   */
  parseSwiftMessage(message: string): SwiftMT103 | SwiftMT202 | SwiftMX | null {
    // In production, use a proper SWIFT parser library
    // This is a simplified parser

    if (message.startsWith('{')) {
      // JSON format (MX/ISO 20022)
      try {
        const parsed = JSON.parse(message);
        if (parsed.Document && parsed.Document.FIToFICstmrCdtTrf) {
          return this.parsePacs008(parsed);
        }
      } catch {
        return null;
      }
    } else {
      // MT format (block-based)
      if (message.includes(':20:')) {
        // MT103 or MT202
        return this.parseMTMessage(message);
      }
    }

    return null;
  }

  private parseMTMessage(message: string): SwiftMT103 | SwiftMT202 | null {
    // Simplified MT parser - in production use proper library
    const lines = message.split('\n');
    const messageType = lines.find((l) => l.startsWith('{1:'))?.substring(3, 6);

    if (messageType === '103') {
      return this.parseMT103(message);
    } else if (messageType === '202') {
      return this.parseMT202(message);
    }

    return null;
  }

  private parseMT103(message: string): SwiftMT103 | null {
    // Simplified - extract key fields
    // In production, use proper SWIFT parser
    return {
      messageType: '103',
      senderBIC: this.extractField(message, '52A') || '',
      receiverBIC: this.extractField(message, '56A') || '',
      transactionReference: this.extractField(message, '20') || '',
      valueDate: this.extractField(message, '32A')?.substring(0, 6) || '',
      currency: this.extractField(message, '32A')?.substring(6, 9) || '',
      amount: this.extractField(message, '32A')?.substring(9) || '',
      orderingCustomer: {
        name: this.extractField(message, '50K') || '',
        account: this.extractField(message, '50A'),
      },
      beneficiaryCustomer: {
        name: this.extractField(message, '59') || '',
        account: this.extractField(message, '59'),
      },
      remittanceInfo: this.extractField(message, '70'),
    };
  }

  private parseMT202(message: string): SwiftMT202 | null {
    // Simplified parser
    return {
      messageType: '202',
      senderBIC: this.extractField(message, '52A') || '',
      receiverBIC: this.extractField(message, '56A') || '',
      transactionReference: this.extractField(message, '20') || '',
      valueDate: this.extractField(message, '32A')?.substring(0, 6) || '',
      currency: this.extractField(message, '32A')?.substring(6, 9) || '',
      amount: this.extractField(message, '32A')?.substring(9) || '',
      senderAccount: this.extractField(message, '52A'),
      receiverAccount: this.extractField(message, '56A'),
      remittanceInfo: this.extractField(message, '72'),
    };
  }

  private parsePacs008(json: any): SwiftMX | null {
    const doc = json.Document?.FIToFICstmrCdtTrf?.GrpHdr;
    const tx = json.Document?.FIToFICstmrCdtTrf?.CdtTrfTxInf?.[0];

    if (!doc || !tx) return null;

    return {
      messageType: 'pacs.008',
      messageId: doc.MsgId,
      creationDateTime: doc.CreDtTm,
      debtor: {
        bicfi: tx.DbtrAgt?.FinInstnId?.BICFI,
        account: tx.DbtrAcct?.Id?.IBAN || tx.DbtrAcct?.Id?.Othr?.Id,
      },
      creditor: {
        bicfi: tx.CdtrAgt?.FinInstnId?.BICFI,
        account: tx.CdtrAcct?.Id?.IBAN || tx.CdtrAcct?.Id?.Othr?.Id,
      },
      amount: {
        currency: tx.IntrBkSttlmAmt?.Ccy,
        amount: tx.IntrBkSttlmAmt?.value,
      },
      valueDate: tx.IntrBkSttlmDt,
      remittanceInfo: tx.RmtInf?.Ustrd?.[0],
    };
  }

  private extractField(message: string, tag: string): string | undefined {
    const regex = new RegExp(`:${tag}:[^:]*`, 'i');
    const match = message.match(regex);
    return match ? match[0].substring(tag.length + 2).trim() : undefined;
  }

  mapParticipant(internalData: unknown): ParticipantCreateRequest {
    // SWIFT messages don't directly map to participants
    // This would be used when creating participants from BIC lookups
    const bic = internalData as string;

    return {
      name: bic, // Would need BIC directory lookup
      bic,
      country: bic.substring(4, 6), // Extract country from BIC
      regulatoryTier: 'Tier1', // Default, would need lookup
      metadata: {
        source: 'SWIFT',
      },
    };
  }

  mapAccount(internalData: unknown): AccountCreateRequest {
    // SWIFT messages contain account info but not full account structure
    const accountInfo = internalData as { iban?: string; account?: string; bic?: string };

    return {
      ownerParticipantId: accountInfo.bic || '', // Would need participant lookup
      counterpartyParticipantId: accountInfo.bic || '',
      ibanOrLocalAccount: accountInfo.iban || accountInfo.account,
      currency: 'USD', // Would need to extract from message
      accountType: AccountType.NOSTRO, // Would need context
      metadata: {
        source: 'SWIFT',
      },
    };
  }

  mapTransfer(internalData: unknown): TransferCreateRequest {
    const swiftMessage = this.parseSwiftMessage(internalData as string);

    if (!swiftMessage) {
      throw new Error('Invalid SWIFT message format');
    }

    if (swiftMessage.messageType === '103' || swiftMessage.messageType === '202') {
      const mt = swiftMessage as SwiftMT103 | SwiftMT202;
      return {
        fromAccountId: mt.senderAccount || '', // Would need account lookup
        toAccountId: mt.receiverAccount || '', // Would need account lookup
        amount: mt.amount,
        currency: mt.currency,
        valueDate: this.parseSwiftDate(mt.valueDate),
        reference: mt.transactionReference,
        metadata: {
          swiftMessageType: mt.messageType,
          swiftReference: mt.transactionReference,
          remittanceInfo: 'remittanceInfo' in mt ? mt.remittanceInfo : undefined,
        },
      };
    } else {
      // MX format
      const mx = swiftMessage as SwiftMX;
      return {
        fromAccountId: mx.debtor.account || '',
        toAccountId: mx.creditor.account || '',
        amount: mx.amount.amount,
        currency: mx.amount.currency,
        valueDate: mx.valueDate.split('T')[0],
        reference: mx.messageId,
        metadata: {
          swiftMessageType: mx.messageType,
          swiftMessageId: mx.messageId,
          remittanceInfo: mx.remittanceInfo,
        },
      };
    }
  }

  mapTransferToInternal(transfer: NostroVostroTransfer): unknown {
    // Generate MT202 format
    return {
      messageType: '202',
      senderBIC: transfer.fromParticipantId,
      receiverBIC: transfer.toParticipantId,
      transactionReference: transfer.transferId,
      valueDate: this.formatSwiftDate(transfer.valueDate),
      currency: transfer.currency,
      amount: transfer.amount,
      remittanceInfo: transfer.reference,
    };
  }

  mapAccountToInternal(account: NostroVostroAccount): unknown {
    // SWIFT doesn't have account structure, return IBAN/local account
    return {
      iban: account.ibanOrLocalAccount,
      account: account.ibanOrLocalAccount,
    };
  }

  async postTransfer(transfer: NostroVostroTransfer): Promise<{ success: boolean; internalId?: string; error?: string }> {
    try {
      const swiftMessage = this.mapTransferToInternal(transfer);
      // In production, send to SWIFT network
      // Mock response
      const internalId = this.generateInternalReference('SWIFT');
      return { success: true, internalId };
    } catch (error) {
      return this.handleError(error, 'postTransfer');
    }
  }

  async getAccountBalance(accountId: string): Promise<{ balance: string; available: string; hold: string }> {
    // SWIFT doesn't provide balance queries directly
    throw new Error('SWIFT adapter does not support balance queries');
  }

  async reconcile(accountId: string, asOfDate: Date): Promise<{ matched: number; breaks: unknown[] }> {
    // SWIFT reconciliation would be done via MT940/MT942 statements
    throw new Error('SWIFT reconciliation requires MT940/MT942 statement processing');
  }

  private parseSwiftDate(dateStr: string): string {
    // Convert SWIFT date format (YYMMDD) to ISO 8601
    if (dateStr.length === 6) {
      const year = '20' + dateStr.substring(0, 2);
      const month = dateStr.substring(2, 4);
      const day = dateStr.substring(4, 6);
      return `${year}-${month}-${day}`;
    }
    return dateStr; // Assume already ISO format
  }

  private formatSwiftDate(date: Date): string {
    // Convert ISO date to SWIFT format (YYMMDD)
    const year = date.getFullYear().toString().substring(2);
    const month = (date.getMonth() + 1).toString().padStart(2, '0');
    const day = date.getDate().toString().padStart(2, '0');
    return `${year}${month}${day}`;
  }
}

