// ISO 20022 Message Adapter
// Maps ISO 20022 messages (pacs.008, pacs.009, camt.*) to DBIS Nostro/Vostro API

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

/**
 * ISO 20022 pacs.008 (Credit Transfer) structure
 */
interface Pacs008 {
  Document: {
    FIToFICstmrCdtTrf: {
      GrpHdr: {
        MsgId: string;
        CreDtTm: string;
        NbOfTxs: string;
      };
      CdtTrfTxInf: Array<{
        PmtId: {
          InstrId: string;
          EndToEndId: string;
        };
        IntrBkSttlmAmt: {
          Ccy: string;
          value: string;
        };
        IntrBkSttlmDt: string;
        Dbtr: {
          Nm: string;
        };
        DbtrAcct: {
          Id: {
            IBAN?: string;
            Othr?: {
              Id: string;
            };
          };
        };
        DbtrAgt: {
          FinInstnId: {
            BICFI?: string;
          };
        };
        Cdtr: {
          Nm: string;
        };
        CdtrAcct: {
          Id: {
            IBAN?: string;
            Othr?: {
              Id: string;
            };
          };
        };
        CdtrAgt: {
          FinInstnId: {
            BICFI?: string;
          };
        };
        RmtInf?: {
          Ustrd?: string[];
        };
      }>;
    };
  };
}

/**
 * ISO 20022 pacs.009 (Direct Debit) structure
 */
interface Pacs009 {
  Document: {
    FIToFICstmrDrctDbt: {
      GrpHdr: {
        MsgId: string;
        CreDtTm: string;
      };
      DrctDbtTxInf: Array<{
        PmtId: {
          InstrId: string;
          EndToEndId: string;
        };
        IntrBkSttlmAmt: {
          Ccy: string;
          value: string;
        };
        IntrBkSttlmDt: string;
        Dbtr: {
          Nm: string;
        };
        DbtrAcct: {
          Id: {
            IBAN?: string;
          };
        };
        DbtrAgt: {
          FinInstnId: {
            BICFI?: string;
          };
        };
        Cdtr: {
          Nm: string;
        };
        CdtrAcct: {
          Id: {
            IBAN?: string;
          };
        };
        CdtrAgt: {
          FinInstnId: {
            BICFI?: string;
          };
        };
      }>;
    };
  };
}

/**
 * ISO 20022 camt.053 (Bank Statement) structure
 */
interface Camt053 {
  Document: {
    BkToCstmrStmt: {
      GrpHdr: {
        MsgId: string;
        CreDtTm: string;
      };
      Stmt: Array<{
        Id: string;
        CreDtTm: string;
        Acct: {
          Id: {
            IBAN?: string;
            Othr?: {
              Id: string;
            };
          };
        };
        Bal: Array<{
          Tp: {
            CdOrPrtry: {
              Cd: string;
            };
          };
          Amt: {
            Ccy: string;
            value: string;
          };
          CdtDbtInd: string;
          Dt: {
            Dt: string;
          };
        }>;
        Ntry: Array<{
          Amt: {
            Ccy: string;
            value: string;
          };
          CdtDbtInd: string;
          BookgDt: {
            Dt: string;
          };
          ValDt: {
            Dt: string;
          };
          NtryRef: string;
          AcctSvcrRef?: string;
        }>;
      }>;
    };
  };
}

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

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

  /**
   * Parse ISO 20022 message
   */
  parseMessage(message: string | object): Pacs008 | Pacs009 | Camt053 | null {
    let parsed: any;

    if (typeof message === 'string') {
      try {
        parsed = JSON.parse(message);
      } catch {
        // Try XML parsing
        // In production, use proper XML parser
        return null;
      }
    } else {
      parsed = message;
    }

    // Detect message type
    if (parsed.Document?.FIToFICstmrCdtTrf) {
      return parsed as Pacs008;
    } else if (parsed.Document?.FIToFICstmrDrctDbt) {
      return parsed as Pacs009;
    } else if (parsed.Document?.BkToCstmrStmt) {
      return parsed as Camt053;
    }

    return null;
  }

  mapParticipant(internalData: unknown): ParticipantCreateRequest {
    // Extract from BICFI in ISO 20022 message
    const bicfi = internalData as string;

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

  mapAccount(internalData: unknown): AccountCreateRequest {
    const accountInfo = internalData as { iban?: string; account?: string; bicfi?: string };

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

  mapTransfer(internalData: unknown): TransferCreateRequest {
    const message = this.parseMessage(internalData as string | object);

    if (!message) {
      throw new Error('Invalid ISO 20022 message format');
    }

    if ('FIToFICstmrCdtTrf' in message.Document) {
      // pacs.008
      const pacs008 = message as Pacs008;
      const tx = pacs008.Document.FIToFICstmrCdtTrf.CdtTrfTxInf[0];

      return {
        fromAccountId: tx.DbtrAcct?.Id?.IBAN || tx.DbtrAcct?.Id?.Othr?.Id || '',
        toAccountId: tx.CdtrAcct?.Id?.IBAN || tx.CdtrAcct?.Id?.Othr?.Id || '',
        amount: tx.IntrBkSttlmAmt.value,
        currency: tx.IntrBkSttlmAmt.Ccy,
        valueDate: tx.IntrBkSttlmDt.split('T')[0],
        reference: tx.PmtId.EndToEndId,
        metadata: {
          iso20022MessageType: 'pacs.008',
          messageId: pacs008.Document.FIToFICstmrCdtTrf.GrpHdr.MsgId,
          instructionId: tx.PmtId.InstrId,
          endToEndId: tx.PmtId.EndToEndId,
          remittanceInfo: tx.RmtInf?.Ustrd?.[0],
        },
      };
    } else if ('FIToFICstmrDrctDbt' in message.Document) {
      // pacs.009
      const pacs009 = message as Pacs009;
      const tx = pacs009.Document.FIToFICstmrDrctDbt.DrctDbtTxInf[0];

      return {
        fromAccountId: tx.DbtrAcct?.Id?.IBAN || '',
        toAccountId: tx.CdtrAcct?.Id?.IBAN || '',
        amount: tx.IntrBkSttlmAmt.value,
        currency: tx.IntrBkSttlmAmt.Ccy,
        valueDate: tx.IntrBkSttlmDt.split('T')[0],
        reference: tx.PmtId.EndToEndId,
        metadata: {
          iso20022MessageType: 'pacs.009',
          messageId: pacs009.Document.FIToFICstmrDrctDbt.GrpHdr.MsgId,
          instructionId: tx.PmtId.InstrId,
          endToEndId: tx.PmtId.EndToEndId,
        },
      };
    }

    throw new Error('Unsupported ISO 20022 message type');
  }

  mapTransferToInternal(transfer: NostroVostroTransfer): unknown {
    // Generate pacs.008 format
    return {
      Document: {
        FIToFICstmrCdtTrf: {
          GrpHdr: {
            MsgId: `DBIS-${transfer.transferId}`,
            CreDtTm: new Date().toISOString(),
            NbOfTxs: '1',
          },
          CdtTrfTxInf: [
            {
              PmtId: {
                InstrId: transfer.transferId,
                EndToEndId: transfer.reference || transfer.transferId,
              },
              IntrBkSttlmAmt: {
                Ccy: transfer.currency,
                value: transfer.amount,
              },
              IntrBkSttlmDt: transfer.valueDate.toISOString(),
              Dbtr: {
                Nm: transfer.fromParticipantId,
              },
              DbtrAcct: {
                Id: {
                  IBAN: transfer.fromAccountId,
                },
              },
              DbtrAgt: {
                FinInstnId: {
                  BICFI: transfer.fromParticipantId,
                },
              },
              Cdtr: {
                Nm: transfer.toParticipantId,
              },
              CdtrAcct: {
                Id: {
                  IBAN: transfer.toAccountId,
                },
              },
              CdtrAgt: {
                FinInstnId: {
                  BICFI: transfer.toParticipantId,
                },
              },
              RmtInf: transfer.reference
                ? {
                    Ustrd: [transfer.reference],
                  }
                : undefined,
            },
          ],
        },
      },
    };
  }

  mapAccountToInternal(account: NostroVostroAccount): unknown {
    // Return camt.053 account structure
    return {
      Document: {
        BkToCstmrStmt: {
          Stmt: [
            {
              Acct: {
                Id: {
                  IBAN: account.ibanOrLocalAccount,
                },
              },
              Bal: [
                {
                  Tp: {
                    CdOrPrtry: {
                      Cd: 'CLBD',
                    },
                  },
                  Amt: {
                    Ccy: account.currency,
                    value: account.currentBalance,
                  },
                  CdtDbtInd: 'CRDT',
                  Dt: {
                    Dt: account.lastUpdatedAt.toISOString(),
                  },
                },
              ],
            },
          ],
        },
      },
    };
  }

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

  async getAccountBalance(accountId: string): Promise<{ balance: string; available: string; hold: string }> {
    // ISO 20022 doesn't provide direct balance queries
    // Would need camt.053 statement request
    throw new Error('ISO 20022 adapter requires camt.053 statement for balance queries');
  }

  async reconcile(accountId: string, asOfDate: Date): Promise<{ matched: number; breaks: unknown[] }> {
    // ISO 20022 reconciliation uses camt.054 (Debit/Credit Notification)
    // or camt.053 (Statement)
    throw new Error('ISO 20022 reconciliation requires camt.053/camt.054 processing');
  }
}

