import { v4 as uuidv4 } from 'uuid';
import {
  buildMoneySupplySnapshot,
  GL_CODES,
  m2FiatToTokenLoadAmount,
  type TokenLoadRequest,
  type SettlementRecord,
} from '@dbis/settlement-core';
import { loadConfig, loadOfficeProfile, settlementOfficeId } from '../config';
import { fetchGlBalances, postJournalEntry, resolveGlAccountId } from '../adapters/fineract';
import { requestTokenMint } from '../adapters/omnl';
import { settlementStore } from '../store/settlement-store';

export async function processTokenLoad(input: TokenLoadRequest): Promise<SettlementRecord> {
  const cfg = loadConfig();
  const profile = loadOfficeProfile();
  const officeId = settlementOfficeId(input.officeId);
  const req = { ...input, officeId };

  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: 'INTERNAL-M2-TOKEN-LOAD',
      beneficiaryName: req.recipientAddress,
      remittanceInfo: `M2 token load ${req.lineId} → ${req.symbol ?? req.tokenAddress ?? 'token'}`,
      moneyLayers: ['M2'],
      rail: 'INTERNAL',
      convertToCrypto: {
        enabled: true,
        targetChainId: 138,
        tokenLineId: req.lineId,
        recipientAddress: req.recipientAddress,
      },
    },
    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 {
    advance('VALIDATED');
    advance('VERBIAGE_ROLLED', {
      verbiageDocument: [
        `OMNL M2 fiat-backed token load`,
        `Office ${officeId} · line ${req.lineId}`,
        `Amount ${req.amount} · recipient ${req.recipientAddress}`,
        `GL ${GL_CODES.M2_BROAD} → ${GL_CODES.CRYPTO_TOKEN_LIABILITY}`,
      ].join('\n'),
    });

    const balances = await fetchGlBalances(officeId);
    const snap = buildMoneySupplySnapshot(officeId, balances);
    const { loadAmount, fullyBacked } = m2FiatToTokenLoadAmount(req.amount, snap.M2.broadMoney);
    if (!fullyBacked) {
      record.errors.push(`M2 backing insufficient (${loadAmount}/${req.amount})`);
      advance('FAILED');
      return record;
    }

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

    advance('HYBX_RAIL_DISPATCHED');
    advance('ISO20022_ARCHIVED');

    const mint = await requestTokenMint({
      lineId: req.lineId,
      amount: loadAmount,
      recipient: req.recipientAddress,
      settlementRef: record.settlementId,
      dryRun: !cfg.production.allowChainMintExecute,
      symbol: req.symbol,
      tokenAddress: req.tokenAddress,
    });
    advance('CHAIN_MINT_REQUESTED', { chainTxHash: mint.txHash });
    advance('SETTLED');
    return record;
  } catch (err) {
    record.errors.push(err instanceof Error ? err.message : String(err));
    advance('FAILED');
    return record;
  }
}
