import { v4 as uuidv4 } from 'uuid';
import { GL_CODES, type TransferRequest, type SettlementRecord } from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { HybxProductionRail } from '../adapters/hybx-production';
import { archiveIso20022, requestTokenTransfer } from '../adapters/omnl';
import { buildMt103Stub, forwardSwiftToListener } from '../adapters/swift';
import { settlementStore } from '../store/settlement-store';

export async function processTransfer(input: TransferRequest): Promise<SettlementRecord> {
  const cfg = loadConfig();
  const profile = loadOfficeProfile();
  const officeId = settlementOfficeId(input.officeId);
  const req = { ...input, officeId };
  const isExternal = req.rail === 'SWIFT' || req.rail === 'RTGS';

  const existing = settlementStore.getByIdempotencyKey(req.idempotencyKey);
  if (existing?.phase === 'SETTLED') return existing;

  const now = new Date().toISOString();
  let record: SettlementRecord = existing ?? {
    settlementId: uuidv4(),
    idempotencyKey: req.idempotencyKey,
    phase: 'RECEIVED',
    request: {
      idempotencyKey: req.idempotencyKey,
      officeId,
      valueDate: now.slice(0, 10),
      currency: req.currency ?? 'USD',
      amount: req.amount,
      creditorIban: req.creditorIban ?? (isExternal ? '' : 'INTERNAL-CHAIN138'),
      beneficiaryName: req.beneficiaryName ?? req.recipientAddress,
      remittanceInfo: req.remittanceInfo ?? `Transfer ${req.tokenSymbol} → ${req.recipientAddress}`,
      moneyLayers: req.moneyLayers.length ? req.moneyLayers : ['M2'],
      rail: req.rail === 'CHAIN138' ? 'CHAIN138' : req.rail === 'INTERNAL' ? 'INTERNAL' : 'SWIFT',
    },
    errors: [],
    createdAt: now,
    updatedAt: now,
  };

  const advance = (phase: SettlementRecord['phase'], patch: Partial<SettlementRecord> = {}) => {
    record = { ...record, phase, updatedAt: new Date().toISOString(), ...patch };
    settlementStore.save(record);
  };

  try {
    if (isExternal && !req.creditorIban) {
      throw new Error('creditorIban required for external transfer');
    }
    advance('VALIDATED');
    advance('VERBIAGE_ROLLED', {
      verbiageDocument: [
        `OMNL ${req.rail} transfer · ${req.tokenSymbol}`,
        `M2 layer · ${req.amount}`,
        `To ${req.recipientAddress}`,
        isExternal ? `IBAN ${req.creditorIban}` : 'Internal / Chain 138',
      ].join('\n'),
    });

    const amount = parseFloat(req.amount) || 0;
    if (amount > 0) {
      const [debitGlId, creditGlId] = await Promise.all([
        resolveGlAccountId(profile.settlement.glM2 ?? GL_CODES.M2_BROAD),
        resolveGlAccountId(profile.settlement.glSettlement ?? GL_CODES.SETTLEMENT_SUSPENSE),
      ]);
      const je = await postJournalEntry({
        officeId,
        transactionDate: now.slice(0, 10),
        referenceNumber: req.idempotencyKey,
        comments: `${req.rail} transfer ${req.tokenSymbol}`,
        debits: [{ glAccountId: debitGlId, amount }],
        credits: [{ glAccountId: creditGlId, amount }],
      });
      advance('FINERACT_POSTED', { fineractJournalRef: String(je.resourceId) });
    } else {
      advance('FINERACT_POSTED');
    }

    if (isExternal && cfg.rails.hybx.enabled) {
      const hybx = new HybxProductionRail();
      const pay = await hybx.dispatchPayment({
        idempotencyKey: req.idempotencyKey,
        amount: req.amount,
        currency: req.currency ?? 'USD',
        creditorIban: req.creditorIban!,
        beneficiaryName: req.beneficiaryName ?? req.recipientAddress,
        remittanceInfo: req.remittanceInfo ?? `OMNL ${req.tokenSymbol} external transfer`,
        rail: req.rail === 'RTGS' ? 'RTGS' : 'SWIFT',
      });
      advance('HYBX_RAIL_DISPATCHED', { hybxPaymentId: pay.paymentId });
    } else {
      advance('HYBX_RAIL_DISPATCHED');
    }

    if (isExternal && cfg.rails.swift.enabled && req.creditorIban) {
      const swiftRaw = buildMt103Stub({
        senderRef: req.idempotencyKey,
        currency: req.currency ?? 'USD',
        amount: req.amount,
        valueDate: now.slice(0, 10),
        orderingCustomer: cfg.omnlLegalName,
        beneficiary: req.beneficiaryName ?? req.recipientAddress,
        iban: req.creditorIban,
        remittance: req.remittanceInfo ?? `OMNL ${req.tokenSymbol}`,
      });
      await forwardSwiftToListener(swiftRaw);
      const iso = await archiveIso20022({
        messageType: 'pacs.008',
        direction: 'OUTBOUND',
        raw: swiftRaw,
        settlementRef: record.settlementId,
      });
      advance('ISO20022_ARCHIVED', { iso20022MessageId: iso.messageId });
    } else {
      advance('ISO20022_ARCHIVED');
    }

    if (!isExternal && req.tokenAddress && cfg.production.allowChainMintExecute) {
      const xfer = await requestTokenTransfer({
        tokenAddress: req.tokenAddress,
        amount: req.amount,
        recipient: req.recipientAddress,
        settlementRef: record.settlementId,
        dryRun: false,
        symbol: req.tokenSymbol,
      });
      advance('CHAIN_MINT_REQUESTED', {
        chainTxHash: xfer.txHash,
        verbiageDocument: [
          record.verbiageDocument,
          `On-chain ERC-20 transfer: ${req.tokenAddress} → ${req.recipientAddress}`,
          xfer.txHash ? `tx ${xfer.txHash}` : `status ${xfer.status}`,
        ].join('\n'),
      });
    } else {
      advance('CHAIN_MINT_REQUESTED', {
        verbiageDocument: [
          record.verbiageDocument,
          isExternal
            ? 'Bank rail (SWIFT/IBAN) — no on-chain transfer'
            : `On-chain ERC-20 transfer prepared: ${req.tokenAddress} → ${req.recipientAddress}`,
        ].join('\n'),
      });
    }
    advance('SETTLED');
    return record;
  } catch (err) {
    record.errors.push(err instanceof Error ? err.message : String(err));
    advance('FAILED');
    return record;
  }
}
