/**
 * SBV ISO 20022 Message Transformer
 * Converts legacy SBV-IBPS XML format ↔ ISO 20022 MX (pacs / pain / camt)
 *
 * Cơ quan chủ quản: Ngân hàng Nhà nước Việt Nam (SBV)
 * Phiên bản: 1.0.0
 */

import { parseStringPromise, Builder } from 'xml2js';
import { v4 as uuidv4 } from 'uuid';
import { createHash } from 'crypto';

// ─── Constants ───────────────────────────────────────────────────────────────

/** BIC chính thức của SBV (Ngân hàng Nhà nước Việt Nam) */
const SBV_BIC = 'SBVNVNVX';

/** Namespace ISO 20022 pacs.008 */
const NS_PACS008 = 'urn:iso:std:iso:20022:tech:xsd:pacs.008.001.09';

/** Namespace ISO 20022 pacs.002 */
const NS_PACS002 = 'urn:iso:std:iso:20022:tech:xsd:pacs.002.001.11';

/** Namespace ISO 20022 head.001 */
const NS_HEAD001 = 'urn:iso:std:iso:20022:tech:xsd:head.001.001.03';

// ─── Types ───────────────────────────────────────────────────────────────────

export interface IbpsTransferMessage {
  IBPS_MSG_ID: string;
  TRANS_DATE: string;         // YYYYMMDD
  TRANS_TIME: string;         // HHmmss
  SENDER_BNK_CODE: string;    // Mã ngân hàng SBV nội địa
  RECEIVER_BNK_CODE: string;
  SENDER_ACCT: string;
  RECEIVER_ACCT: string;
  SENDER_NAME: string;
  RECEIVER_NAME: string;
  AMOUNT: string;             // Số nguyên (VND, không có thập phân)
  CONTENT: string;            // Nội dung chuyển tiền
  TRANS_TYPE: string;         // '10' = Nội tỉnh, '20' = Liên tỉnh
}

export interface Iso20022Pacs008 {
  Document: {
    $: { xmlns: string };
    FIToFICstmrCdtTrf: {
      GrpHdr: Iso20022GroupHeader;
      CdtTrfTxInf: Iso20022CreditTransferTxInfo[];
    };
  };
}

export interface Iso20022GroupHeader {
  MsgId: string;
  CreDtTm: string;
  NbOfTxs: string;
  SttlmInf: { SttlmMtd: string; ClrSys?: { Cd: string } };
  InstgAgt: { FinInstnId: { BICFI: string } };
  InstdAgt: { FinInstnId: { BICFI: string } };
}

export interface Iso20022CreditTransferTxInfo {
  PmtId: { InstrId: string; EndToEndId: string; UETR: string; TxId: string };
  IntrBkSttlmAmt: { _: string; $: { Ccy: string } };
  IntrBkSttlmDt: string;
  InstdAmt?:     { _: string; $: { Ccy: string } };
  Purp?:         { Cd: string };
  RmtInf?:       { Ustrd: string };
  Dbtr:          { Nm: string };
  DbtrAcct:      { Id: { Othr: { Id: string } } };
  DbtrAgt:       { FinInstnId: { BICFI: string; LEI?: string } };
  Cdtr:          { Nm: string };
  CdtrAcct:      { Id: { Othr: { Id: string } } };
  CdtrAgt:       { FinInstnId: { BICFI: string; LEI?: string } };
}

export interface TransformResult {
  isoXml:       string;   // ISO 20022 pacs.008 XML output
  businessAppHdr: string; // head.001 Business Application Header
  messageHash:  string;   // SHA-256 hash for on-chain anchoring
  uetr:         string;   // Unique End-to-end Transaction Reference
}

// ─── BIC Mapping (IBPS code → ISO 9362 BIC) ─────────────────────────────────

const IBPS_TO_BIC: Record<string, string> = {
  '970436': 'VBAAVNVX',  // Agribank
  '970415': 'VBINVNVX',  // VietinBank (CTG)
  '970418': 'BIDVVNVX',  // BIDV
  '970432': 'VPCBVNVX',  // VPBank
  '970407': 'TPBVVNVX',  // TPBank
  '970448': 'ORICVNVX',  // OCB
  '970422': 'MSCBVNVX',  // MSB
  '970446': 'VPBKVNVX',  // VPBank (alt)
  '970423': 'TIHBVNVX',  // Techcombank
  '970406': 'SACLVNVX',  // Sacombank
  '970403': 'SEAVVNVX',  // SeABank
  '970438': 'BFTVVNVX',  // Bao Viet Bank
  '970431': 'EXIMVNVX',  // Eximbank
  '970400': 'HVBKVNVX',  // SHB
  '970414': 'OCBCVNVX',  // OCB alt
  '000000': SBV_BIC,     // SBV (Ngân hàng Nhà nước)
};

/**
 * Tra cứu BIC từ mã ngân hàng IBPS nội địa.
 * Trả về Unknown-BIC có cấu trúc hợp lệ nếu không tìm thấy.
 */
function resolveBIC(ibpsCode: string): string {
  return IBPS_TO_BIC[ibpsCode] ?? `UNKN${ibpsCode.padStart(4, '0').slice(0, 4)}VN`;
}

/**
 * Chuyển đổi ngày IBPS (YYYYMMDD) → ISO 8601 datetime (UTC+7).
 */
function ibpsDateToIso(date: string, time: string = '000000'): string {
  const y = date.slice(0, 4), m = date.slice(4, 6), d = date.slice(6, 8);
  const hh = time.slice(0, 2), mm = time.slice(2, 4), ss = time.slice(4, 6);
  return `${y}-${m}-${d}T${hh}:${mm}:${ss}+07:00`;
}

/**
 * Chuyển đổi số tiền IBPS (chuỗi số nguyên VND) → decimal string cho ISO 20022.
 */
function ibpsAmountToIso(amount: string): string {
  const num = parseInt(amount, 10);
  if (isNaN(num)) throw new Error(`Invalid IBPS amount: ${amount}`);
  return (num / 100).toFixed(2);   // IBPS lưu đơn vị đồng * 100 (xu)
}

// ─── Core Transformer ────────────────────────────────────────────────────────

/**
 * Biến đổi một IBPS transfer message → ISO 20022 pacs.008 + head.001
 */
export function ibpsToIso20022Pacs008(ibps: IbpsTransferMessage): TransformResult {
  const uetr      = uuidv4();
  const msgId     = `SBV${ibps.TRANS_DATE}${ibps.IBPS_MSG_ID.slice(0, 15)}`;
  const senderBic = resolveBIC(ibps.SENDER_BNK_CODE);
  const recvBic   = resolveBIC(ibps.RECEIVER_BNK_CODE);
  const creDtTm   = ibpsDateToIso(ibps.TRANS_DATE, ibps.TRANS_TIME);
  const settleDt  = `${ibps.TRANS_DATE.slice(0, 4)}-${ibps.TRANS_DATE.slice(4, 6)}-${ibps.TRANS_DATE.slice(6, 8)}`;
  const amount    = ibpsAmountToIso(ibps.AMOUNT);

  const pacs008: Iso20022Pacs008 = {
    Document: {
      $: { xmlns: NS_PACS008 },
      FIToFICstmrCdtTrf: {
        GrpHdr: {
          MsgId:   msgId,
          CreDtTm: creDtTm,
          NbOfTxs: '1',
          SttlmInf: {
            SttlmMtd: 'CLRG',
            ClrSys:   { Cd: 'VNIBPS' }
          },
          InstgAgt: { FinInstnId: { BICFI: senderBic } },
          InstdAgt: { FinInstnId: { BICFI: recvBic   } },
        },
        CdtTrfTxInf: [
          {
            PmtId: {
              InstrId:     ibps.IBPS_MSG_ID,
              EndToEndId:  ibps.IBPS_MSG_ID,
              UETR:        uetr,
              TxId:        `TXN${ibps.IBPS_MSG_ID}`,
            },
            IntrBkSttlmAmt: { _: amount, $: { Ccy: 'VND' } },
            IntrBkSttlmDt:  settleDt,
            InstdAmt:        { _: amount, $: { Ccy: 'VND' } },
            Purp:            { Cd: mapTransType(ibps.TRANS_TYPE) },
            RmtInf:          { Ustrd: ibps.CONTENT.slice(0, 140) },
            Dbtr:            { Nm: ibps.SENDER_NAME.slice(0, 140) },
            DbtrAcct:        { Id: { Othr: { Id: ibps.SENDER_ACCT } } },
            DbtrAgt:         { FinInstnId: { BICFI: senderBic } },
            Cdtr:            { Nm: ibps.RECEIVER_NAME.slice(0, 140) },
            CdtrAcct:        { Id: { Othr: { Id: ibps.RECEIVER_ACCT } } },
            CdtrAgt:         { FinInstnId: { BICFI: recvBic } },
          }
        ]
      }
    }
  };

  const builder  = new Builder({ xmldec: { version: '1.0', encoding: 'UTF-8' } });
  const isoXml   = builder.buildObject(pacs008);
  const msgHash  = createHash('sha256').update(isoXml).digest('hex');
  const appHdr   = buildBusinessAppHeader(msgId, creDtTm, NS_PACS008, senderBic, recvBic);

  return { isoXml, businessAppHdr: appHdr, messageHash: msgHash, uetr };
}

/**
 * Ánh xạ mã loại giao dịch IBPS → ISO 20022 Purpose Code
 */
function mapTransType(transType: string): string {
  const map: Record<string, string> = {
    '10': 'INTC',  // Nội tỉnh → IntraCompany
    '20': 'TRAD',  // Liên tỉnh → Trade
    '30': 'SALA',  // Lương
    '40': 'TAXS',  // Thuế
    '50': 'GOVT',  // Chi ngân sách nhà nước
    '60': 'PENS',  // Lương hưu / trợ cấp
  };
  return map[transType] ?? 'OTHR';
}

/**
 * Tạo Business Application Header (head.001.001.03)
 */
function buildBusinessAppHeader(
  msgId:    string,
  creDtTm:  string,
  msgDefId: string,
  fromBic:  string,
  toBic:    string
): string {
  const header = {
    AppHdr: {
      $:       { xmlns: NS_HEAD001 },
      Fr:      { FIId: { FinInstnId: { BICFI: fromBic } } },
      To:      { FIId: { FinInstnId: { BICFI: toBic   } } },
      BizMsgIdr: msgId,
      MsgDefIdr: msgDefId,
      CreDt:   creDtTm,
    }
  };
  return new Builder({ xmldec: { version: '1.0', encoding: 'UTF-8' } }).buildObject(header);
}

// ─── Reverse Transformer (ISO 20022 → IBPS) ──────────────────────────────────

/**
 * Biến đổi ISO 20022 pacs.008 XML → IBPS nội địa (backward compatibility)
 * Dùng trong giai đoạn transition khi core banking chưa upgrade xong
 */
export async function iso20022Pacs008ToIbps(xmlString: string): Promise<IbpsTransferMessage> {
  const parsed = await parseStringPromise(xmlString, { explicitArray: false });
  const doc    = parsed.Document?.FIToFICstmrCdtTrf;
  if (!doc) throw new Error('Invalid pacs.008 document structure');

  const txInfo   = Array.isArray(doc.CdtTrfTxInf) ? doc.CdtTrfTxInf[0] : doc.CdtTrfTxInf;
  const grpHdr   = doc.GrpHdr;
  const creDt    = (grpHdr.CreDtTm as string).replace(/[-:T+]/g, '');
  const senderBic = grpHdr.InstgAgt?.FinInstnId?.BICFI ?? '';
  const recvBic   = grpHdr.InstdAgt?.FinInstnId?.BICFI ?? '';

  const amtStr   = txInfo.IntrBkSttlmAmt?._ ?? txInfo.IntrBkSttlmAmt;
  const amountCents = Math.round(parseFloat(amtStr) * 100).toString();

  return {
    IBPS_MSG_ID:       txInfo.PmtId?.InstrId ?? grpHdr.MsgId,
    TRANS_DATE:        creDt.slice(0, 8),
    TRANS_TIME:        creDt.slice(8, 14),
    SENDER_BNK_CODE:   reverseBIC(senderBic),
    RECEIVER_BNK_CODE: reverseBIC(recvBic),
    SENDER_ACCT:       txInfo.DbtrAcct?.Id?.Othr?.Id ?? '',
    RECEIVER_ACCT:     txInfo.CdtrAcct?.Id?.Othr?.Id ?? '',
    SENDER_NAME:       txInfo.Dbtr?.Nm ?? '',
    RECEIVER_NAME:     txInfo.Cdtr?.Nm ?? '',
    AMOUNT:            amountCents,
    CONTENT:           txInfo.RmtInf?.Ustrd ?? '',
    TRANS_TYPE:        reverseMapPurpose(txInfo.Purp?.Cd),
  };
}

/**
 * Tra cứu mã IBPS từ BIC (reverse lookup)
 */
function reverseBIC(bic: string): string {
  return Object.entries(IBPS_TO_BIC).find(([, v]) => v === bic)?.[0] ?? '000000';
}

function reverseMapPurpose(purposeCd: string | undefined): string {
  const map: Record<string, string> = { INTC: '10', TRAD: '20', SALA: '30', TAXS: '40', GOVT: '50', PENS: '60' };
  return map[purposeCd ?? ''] ?? '10';
}
