import { ethers } from 'ethers';
import {
  MSG_TYPE,
  authAttestationHashFromProof,
  hashShortUtf8,
  type CanonicalPaymentMessage,
} from '@dbis/checkpoint-core';
import type { ParsedInbound } from '../types';

function msgTypeCode(parsed: ParsedInbound): CanonicalPaymentMessage['msgTypeCode'] {
  if (parsed.format === 'ktt') return MSG_TYPE.KTT;
  if (parsed.format === 'swift_fin') {
    if (parsed.messageType === 'MT103') return MSG_TYPE.MT103;
    return MSG_TYPE.UNKNOWN;
  }
  if (parsed.format === 'iso20022') {
    if (parsed.messageType === 'pacs.008') return MSG_TYPE.PACS008;
    if (parsed.messageType === 'pain.001') return MSG_TYPE.PAIN001;
  }
  return MSG_TYPE.UNKNOWN;
}

function msgTypeLabel(parsed: ParsedInbound): CanonicalPaymentMessage['msgType'] {
  if (parsed.format === 'ktt') return parsed.messageType;
  if (parsed.format === 'swift_fin' && parsed.messageType === 'MT103') return 'MT103';
  if (parsed.format === 'iso20022' && parsed.messageType === 'pacs.008') return 'pacs.008';
  if (parsed.format === 'iso20022' && parsed.messageType === 'pain.001') return 'pain.001';
  return 'chain138.synthetic';
}

function settlementMethod(parsed: ParsedInbound): string {
  if (parsed.format === 'ktt') return 'KTT';
  if (parsed.format === 'swift_fin') return 'SWIFT';
  return 'ISO20022';
}

function pickString(parsed: ParsedInbound, ...keys: string[]): string {
  const obj = parsed.parsed;
  for (const k of keys) {
    const v = obj[k];
    if (typeof v === 'string' && v.trim()) return v.trim();
  }
  return '';
}

export function mapToCanonical(parsed: ParsedInbound, dedupeKey: string): CanonicalPaymentMessage | undefined {
  const instructionId =
    pickString(parsed, 'instructionId', 'senderReference', 'transactionReference', 'messageId') ||
    `KTT-${dedupeKey.slice(0, 16)}`;
  const endToEndId = pickString(parsed, 'endToEndId', 'transactionReference', 'senderReference') || instructionId;
  const msgId = pickString(parsed, 'messageId', 'senderReference') || instructionId;
  const uetr = pickString(parsed, 'uetr') || '';
  const debtorId =
    pickString(parsed, 'orderingCustomer', 'debtor', 'sendingInstitution', 'debtorId') || 'UNKNOWN';
  const creditorId =
    pickString(parsed, 'beneficiaryCustomer', 'creditor', 'beneficiaryInstitution', 'creditorId') ||
    'UNKNOWN';
  const currencyCode = pickString(parsed, 'currency', 'currencyCode') || 'USD';
  const amountRaw = pickString(parsed, 'amount') || '0';
  const purpose =
    pickString(parsed, 'remittanceInfo', 'remittance', 'purpose', 'senderToReceiverInfo', 'narrative') ||
    `${parsed.format}/${parsed.messageType}`;

  const instructionIdBytes32 = ethers.keccak256(ethers.toUtf8Bytes(`INSTR:${instructionId}`));
  const uetrBytes32 = uetr
    ? ethers.keccak256(ethers.toUtf8Bytes(`UETR:${uetr}`))
    : ethers.keccak256(ethers.toUtf8Bytes(`UETR:${dedupeKey}`));

  let authAttestationHash: string | undefined;
  if (parsed.format === 'ktt') {
    const fromParsed = pickString(parsed, 'authAttestationHash');
    if (fromParsed) {
      authAttestationHash = fromParsed.startsWith('0x') ? fromParsed : hashShortUtf8(fromParsed);
    } else if (parsed.authAttestationHash) {
      authAttestationHash = parsed.authAttestationHash;
    } else if (parsed.testKeyProof && pickString(parsed, 'normalizedBody')) {
      authAttestationHash = authAttestationHashFromProof(
        parsed.testKeyProof,
        pickString(parsed, 'normalizedBody')
      );
    }
  }

  const base: Omit<CanonicalPaymentMessage, 'payloadHash'> = {
    msgType: msgTypeLabel(parsed),
    msgTypeCode: msgTypeCode(parsed),
    instructionId,
    instructionIdBytes32,
    endToEndId,
    endToEndIdHash: hashShortUtf8(endToEndId),
    msgId,
    uetr: uetr || dedupeKey.slice(0, 36),
    uetrBytes32,
    accountRefId: debtorId,
    counterpartyRefId: creditorId,
    debtorId,
    creditorId,
    purpose,
    settlementMethod: settlementMethod(parsed),
    categoryPurpose: 'CBFF',
    currencyCode,
    amountRaw,
    amountSmallestUnit: amountRaw.replace(/[,.]/g, '') || '0',
    debtorRefHash: hashShortUtf8(debtorId),
    creditorRefHash: hashShortUtf8(creditorId),
    purposeHash: hashShortUtf8(purpose),
    chain138TxHash: pickString(parsed, 'txId', 'chain138TxHash'),
    valueDateIso: pickString(parsed, 'valueDate') || undefined,
    ...(authAttestationHash ? { authAttestationHash } : {}),
  };

  const payloadHash = ethers.keccak256(
    ethers.toUtf8Bytes(JSON.stringify({ ...base, sourceFormat: parsed.format, messageType: parsed.messageType }))
  );

  return { ...base, payloadHash };
}
