/**
 * SBV ISO 20022 API Gateway — Express Router
 * Cổng tiếp nhận & phân phối message ISO 20022 cho toàn bộ hệ thống NHNN
 *
 * Endpoints:
 *   POST /api/v1/iso20022/inbound       - Nhận message từ NHTM → SBV
 *   POST /api/v1/iso20022/outbound      - Gửi message từ SBV → NHTM / mBridge
 *   GET  /api/v1/iso20022/status/:uetr  - Tra trạng thái giao dịch theo UETR
 *   POST /api/v1/iso20022/ibps-migrate  - Chuyển đổi IBPS legacy → ISO 20022
 *   GET  /api/v1/iso20022/health        - Health check
 *
 * Cơ quan chủ quản: Ngân hàng Nhà nước Việt Nam (SBV)
 */

import express, { Request, Response, NextFunction } from 'express';
import { parseStringPromise } from 'xml2js';
import { createHash } from 'crypto';
import { Iso20022Validator, ValidationResult } from '../validator/iso20022-validator';
import { ibpsToIso20022Pacs008, iso20022Pacs008ToIbps, IbpsTransferMessage } from '../transformer/ibps-to-iso20022';
import { logAuditEntry, logValidationFailure } from './audit-logger';
import { anchorToGovChain } from './VNLH-anchor';
import { buildPain001, Pain001Input } from '../transformer/pain001-builder';
import { buildPacs009, Pacs009Input } from '../transformer/pacs009-mbridge-builder';
import { publishMessage } from './kafka-publisher';

export const router = express.Router();
const validator = new Iso20022Validator();

// ─── Middleware: Parse Content-Type ──────────────────────────────────────────

/**
 * Hỗ trợ cả 2 định dạng:
 * - application/xml hoặc text/xml  → parse sang JSON object
 * - application/json               → dùng trực tiếp
 */
async function parseIso20022Body(req: Request, res: Response, next: NextFunction): Promise<void> {
  const ct = req.headers['content-type'] ?? '';
  try {
    if (ct.includes('xml')) {
      const raw = req.body?.toString?.() ?? '';
      req.body = await parseStringPromise(raw, { explicitArray: false, explicitCharkey: true });
    }
    next();
  } catch (err) {
    res.status(400).json({
      code:    'PARSE_ERROR',
      message: 'Không thể parse message body. Kiểm tra định dạng XML/JSON.',
      detail:  (err as Error).message,
    });
  }
}

// ─── POST /inbound ────────────────────────────────────────────────────────────

/**
 * Tiếp nhận message ISO 20022 từ ngân hàng thành viên → SBV routing
 * Header yêu cầu:
 *   X-SBV-MsgDefIdr : Message Definition Identifier  (vd: pacs.008.001.09)
 *   X-SBV-BIC       : BIC của tổ chức gửi
 *   Authorization   : Bearer <token> (OAuth 2.0 client_credentials)
 */
router.post('/inbound', parseIso20022Body, async (req: Request, res: Response) => {
  const msgDefId = req.headers['x-sbv-msgdefidr'] as string;
  const senderBic = req.headers['x-sbv-bic'] as string;

  if (!msgDefId) {
    return res.status(400).json({ code: 'MISSING_HEADER', message: 'Header X-SBV-MsgDefIdr bắt buộc' });
  }

  // 1. Xác thực schema ISO 20022
  const validation: ValidationResult = validator.validate(msgDefId, req.body);

  if (!validation.valid) {
    logValidationFailure(msgDefId, senderBic, validation.errors);
    return res.status(422).json({
      code:     'VALIDATION_FAILED',
      msgDefId,
      errors:   validation.errors,
      warnings: validation.warnings,
    });
  }

  // 2. Tạo hash SHA-256 để anchor lên Gov-Chain
  const msgHash = createHash('sha256')
    .update(JSON.stringify(req.body))
    .digest('hex');

  const uetr       = extractUETRFromBody(req.body);
  const downstream = resolveDownstream(msgDefId, req.body);
  const acceptedAt = new Date().toISOString();

  // 3. Ghi audit log AML/CFT
  logAuditEntry({ msgDefId, uetr, senderBic, msgHash, downstream, warnings: validation.warnings });

  // 4. Neo hash lên Gov-Chain (fire-and-forget, không block response)
  anchorToGovChain(msgHash, uetr, msgDefId).catch(() => undefined);

  // 5. Publish tới Kafka topic tương ứng (fire-and-forget)
  publishMessage({
    msgDefId, uetr, senderBic, msgHash, downstream,
    payload:    req.body,
    receivedAt: acceptedAt,
  }).catch(() => undefined);

  return res.status(202).json({
    code:       'ACCEPTED',
    msgDefId,
    msgHash,
    uetr,
    downstream,
    warnings:    validation.warnings,
    acceptedAt,
  });
});

// ─── POST /outbound/pain001 ───────────────────────────────────────────────────

/**
 * Endpoint khởi tạo lệnh chuyển khoản pain.001 outbound.
 * NHTM gửi JSON → gateway build pain.001 XML + head.001 + neo Gov-Chain.
 * Body: Pain001Input (JSON)
 */
router.post('/outbound/pain001', express.json(), async (req: Request, res: Response) => {
  const input = req.body as Pain001Input;
  const required: (keyof Pain001Input)[] = [
    'msgId', 'executionDate', 'debtorBic', 'debtorAcct', 'debtorName',
    'creditorBic', 'creditorAcct', 'creditorName', 'amount',
  ];
  const missing = required.filter(k => !input[k]);

  if (missing.length > 0) {
    return res.status(400).json({ code: 'MISSING_FIELDS', fields: missing });
  }

  if (typeof input.amount !== 'number' || input.amount <= 0) {
    return res.status(400).json({ code: 'INVALID_AMOUNT', message: 'amount phải là số dương (VND)' });
  }

  try {
    const result = buildPain001(input);

    // Audit log + Gov-Chain anchor
    logAuditEntry({ msgDefId: 'pain.001.001.09', uetr: result.uetr, senderBic: input.debtorBic, msgHash: result.messageHash, downstream: 'NAPAS_DOMESTIC' });
    anchorToGovChain(result.messageHash, result.uetr, 'pain.001.001.09').catch(() => undefined);

    return res.status(200).json({
      code:           'CREATED',
      msgDefId:       'pain.001.001.09',
      uetr:           result.uetr,
      pmtInfId:       result.pmtInfId,
      messageHash:    result.messageHash,
      isoXml:         result.isoXml,
      businessAppHdr: result.businessAppHdr,
    });
  } catch (err) {
    return res.status(500).json({ code: 'BUILD_ERROR', message: (err as Error).message });
  }
});

// ─── POST /outbound/pacs009 (ASEAN mBridge) ──────────────────────────────────

/**
 * Endpoint khởi tạo chuyển vốn liên ngân hàng qua ASEAN mBridge.
 * Body: Pacs009Input (JSON) — bắt buộc có underlyingCustomer (FATF R.16)
 */
router.post('/outbound/pacs009', express.json(), async (req: Request, res: Response) => {
  const input = req.body as Pacs009Input;
  const required: (keyof Pacs009Input)[] = [
    'msgId', 'instructingBic', 'instructedBic', 'amount', 'currency',
    'settlementDate', 'underlyingCustomer',
  ];
  const missing = required.filter(k => !input[k]);

  if (missing.length > 0) {
    return res.status(400).json({ code: 'MISSING_FIELDS', fields: missing });
  }
  if (typeof input.amount !== 'number' || input.amount <= 0) {
    return res.status(400).json({ code: 'INVALID_AMOUNT', message: 'amount phải là số dương' });
  }

  try {
    const result = buildPacs009(input);

    logAuditEntry({
      msgDefId:   'pacs.009.001.09',
      uetr:       result.uetr,
      senderBic:  input.instructingBic,
      msgHash:    result.messageHash,
      downstream: 'ASEAN_MBRIDGE',
    });
    anchorToGovChain(result.messageHash, result.uetr, 'pacs.009.001.09').catch(() => undefined);
    publishMessage({
      msgDefId:   'pacs.009.001.09',
      uetr:       result.uetr,
      senderBic:  input.instructingBic,
      msgHash:    result.messageHash,
      downstream: 'ASEAN_MBRIDGE',
      payload:    input,
      receivedAt: new Date().toISOString(),
    }).catch(() => undefined);

    return res.status(200).json({
      code:           'CREATED',
      msgDefId:       'pacs.009.001.09',
      uetr:           result.uetr,
      messageHash:    result.messageHash,
      downstream:     'ASEAN_MBRIDGE',
      isoXml:         result.isoXml,
      businessAppHdr: result.businessAppHdr,
    });
  } catch (err) {
    return res.status(500).json({ code: 'BUILD_ERROR', message: (err as Error).message });
  }
});

// ─── POST /auth/aml-report ────────────────────────────────────────────────────

/**
 * Endpoint nhận message AML/CFT regulatory reporting (auth.001 / auth.002 / auth.018).
 * NHNN / NHTM gửi message kiểm soát ngoại hối hoặc yêu cầu thông tin AML.
 * Header: X-SBV-MsgDefIdr phải là auth.001.*, auth.002.* hoặc auth.018.*
 */
router.post('/auth/aml-report', parseIso20022Body, async (req: Request, res: Response) => {
  const msgDefId  = req.headers['x-sbv-msgdefidr'] as string;
  const senderBic = req.headers['x-sbv-bic'] as string;

  if (!msgDefId) {
    return res.status(400).json({ code: 'MISSING_HEADER', message: 'Header X-SBV-MsgDefIdr bắt buộc' });
  }
  if (!msgDefId.startsWith('auth.')) {
    return res.status(400).json({
      code:    'INVALID_MSG_TYPE',
      message: `Endpoint này chỉ xử lý auth.* messages. Nhận được: ${msgDefId}`,
    });
  }

  const validation: ValidationResult = validator.validate(msgDefId, req.body);

  if (!validation.valid) {
    logValidationFailure(msgDefId, senderBic, validation.errors);
    return res.status(422).json({
      code:    'VALIDATION_FAILED',
      msgDefId,
      errors:  validation.errors,
      warnings: validation.warnings,
    });
  }

  const msgHash   = createHash('sha256').update(JSON.stringify(req.body)).digest('hex');
  const acceptedAt = new Date().toISOString();

  logAuditEntry({
    msgDefId, senderBic, msgHash,
    downstream: 'SBV_SUPERVISORY',
    warnings: validation.warnings,
    extra: { amlCftReport: true },
  });
  anchorToGovChain(msgHash, null, msgDefId).catch(() => undefined);
  publishMessage({
    msgDefId, senderBic, msgHash,
    downstream: 'SBV_SUPERVISORY',
    payload:    req.body,
    receivedAt: acceptedAt,
  }).catch(() => undefined);

  return res.status(202).json({
    code:       'ACCEPTED',
    msgDefId,
    msgHash,
    downstream: 'SBV_SUPERVISORY',
    warnings:   validation.warnings,
    acceptedAt,
  });
});

// ─── POST /ibps-migrate ───────────────────────────────────────────────────────

/**
 * Endpoint chuyển đổi message IBPS nội địa → ISO 20022 pacs.008
 * Dùng trong giai đoạn transition (core banking chưa nâng cấp xong)
 * Body: IbpsTransferMessage (JSON)
 */
router.post('/ibps-migrate', express.json(), async (req: Request, res: Response) => {
  const ibps = req.body as IbpsTransferMessage;
  const required = ['IBPS_MSG_ID','TRANS_DATE','SENDER_BNK_CODE','RECEIVER_BNK_CODE',
                    'SENDER_ACCT','RECEIVER_ACCT','AMOUNT'];
  const missing = required.filter(k => !ibps[k as keyof IbpsTransferMessage]);

  if (missing.length > 0) {
    return res.status(400).json({ code: 'MISSING_FIELDS', fields: missing });
  }

  try {
    const result = ibpsToIso20022Pacs008(ibps);
    return res.status(200).json({
      code:           'TRANSFORMED',
      msgDefId:       'pacs.008.001.09',
      uetr:           result.uetr,
      messageHash:    result.messageHash,
      isoXml:         result.isoXml,
      businessAppHdr: result.businessAppHdr,
    });
  } catch (err) {
    return res.status(500).json({ code: 'TRANSFORM_ERROR', message: (err as Error).message });
  }
});

// ─── GET /status/:uetr ────────────────────────────────────────────────────────

/**
 * Tra trạng thái giao dịch theo UETR (Unique End-to-end Transaction Reference)
 * Chuẩn SWIFT ISO 20022 gpt-tracker compatible
 */
router.get('/status/:uetr', async (req: Request, res: Response) => {
  const { uetr } = req.params;
  const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

  if (!uuidPattern.test(uetr)) {
    return res.status(400).json({ code: 'INVALID_UETR', message: 'UETR phải là UUID v4 hợp lệ' });
  }

  // TODO: Tra cứu từ transaction store (Redis / PostgreSQL)
  return res.status(200).json({
    uetr,
    status: 'ACCP',          // ISO 20022 status code: Accepted
    statusReason: 'G000',    // SBV reason code
    lastUpdate: new Date().toISOString(),
    chain: {
      anchored: false,        // Set true sau khi Gov-Chain confirm
      blockHash: null,
    },
  });
});

// ─── GET /health ──────────────────────────────────────────────────────────────

router.get('/health', (_req: Request, res: Response) => {
  res.status(200).json({
    status:    'UP',
    version:   '1.0.0',
    gateway:   'SBV-ISO20022',
    timestamp: new Date().toISOString(),
    supportedMessages: [
      // pain — Payment Initiation
      'pain.001', 'pain.002', 'pain.007', 'pain.008', 'pain.013',
      // pacs — Payment Clearing & Settlement
      'pacs.002', 'pacs.003', 'pacs.004', 'pacs.008', 'pacs.009', 'pacs.010',
      // camt — Cash Management
      'camt.052', 'camt.053', 'camt.054', 'camt.055', 'camt.056', 'camt.057', 'camt.060',
      // head & auth
      'head.001', 'auth.001', 'auth.002', 'auth.018',
    ],
    endpoints: {
      inbound:       'POST /api/v1/iso20022/inbound',
      pain001:       'POST /api/v1/iso20022/outbound/pain001',
      pacs009Mbridge:'POST /api/v1/iso20022/outbound/pacs009',
      amlReport:     'POST /api/v1/iso20022/auth/aml-report',
      ibpsMigrate:   'POST /api/v1/iso20022/ibps-migrate',
      status:        'GET  /api/v1/iso20022/status/:uetr',
    },
  });
});

// ─── Helpers ──────────────────────────────────────────────────────────────────

function extractUETRFromBody(body: unknown): string | null {
  const str = JSON.stringify(body);
  const match = str.match(/"UETR":"([0-9a-f-]{36})"/i);
  return match?.[1] ?? null;
}

/**
 * Quyết định downstream routing dựa trên message type và nội dung.
 * pacs.009 → ASEAN mBridge
 * pacs.008 nội tệ VND → NAPAS
 * auth.*   → SBV Supervisory System
 */
function resolveDownstream(msgDefId: string, _body: unknown): string {
  if (msgDefId.startsWith('pacs.009')) return 'ASEAN_MBRIDGE';
  if (msgDefId.startsWith('auth.'))    return 'SBV_SUPERVISORY';
  if (msgDefId.startsWith('camt.'))    return 'SBV_ACCOUNT_MGT';
  return 'NAPAS_DOMESTIC';
}
