"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processExternalTransfer = processExternalTransfer;
exports.getMoneySupply = getMoneySupply;
exports.getSettlementStatus = getSettlementStatus;
const uuid_1 = require("uuid");
const settlement_core_1 = require("@dbis/settlement-core");
const config_1 = require("../config");
const fineract_1 = require("../adapters/fineract");
const hybx_production_1 = require("../adapters/hybx-production");
const omnl_1 = require("../adapters/omnl");
const swift_1 = require("../adapters/swift");
const settlement_store_1 = require("../store/settlement-store");
async function processExternalTransfer(input) {
    const cfg = (0, config_1.loadConfig)();
    const profile = (0, config_1.loadOfficeProfile)();
    const officeId = (0, config_1.settlementOfficeId)(input.officeId);
    const req = { ...input, officeId };
    const existing = settlement_store_1.settlementStore.getByIdempotencyKey(req.idempotencyKey);
    if (existing?.phase === 'SETTLED')
        return existing;
    const now = new Date().toISOString();
    let record = existing ?? {
        settlementId: (0, uuid_1.v4)(),
        idempotencyKey: req.idempotencyKey,
        phase: 'RECEIVED',
        request: req,
        errors: [],
        createdAt: now,
        updatedAt: now,
    };
    const advance = (phase, patch = {}) => {
        record = { ...record, phase, updatedAt: new Date().toISOString(), ...patch };
        settlement_store_1.settlementStore.save(record);
    };
    try {
        if (!(0, settlement_core_1.isIbanValid)(req.creditorIban)) {
            throw new Error(`Invalid creditor IBAN: ${req.creditorIban}`);
        }
        advance('VALIDATED');
        const verbiage = (0, settlement_core_1.rollExternalTransferVerbiage)(req);
        advance('VERBIAGE_ROLLED', { verbiageDocument: verbiage });
        const amount = parseFloat(req.amount) || 0;
        const tf = req.tradeFinance;
        const gl = tf ? (0, settlement_core_1.resolveInstrumentGl)(tf.instrument) : null;
        const debitGl = gl?.debitGl ?? profile.settlement.glSettlement ?? cfg.moneySupply.glSettlement;
        const creditGl = gl?.creditGl ?? profile.settlement.glM1 ?? cfg.moneySupply.glM1;
        if (amount > 0) {
            const [debitGlId, creditGlId] = await Promise.all([
                (0, fineract_1.resolveGlAccountId)(debitGl),
                (0, fineract_1.resolveGlAccountId)(creditGl),
            ]);
            const je = await (0, fineract_1.postJournalEntry)({
                officeId: req.officeId,
                transactionDate: req.valueDate,
                referenceNumber: req.idempotencyKey,
                comments: verbiage.slice(0, 500),
                debits: [{ glAccountId: debitGlId, amount }],
                credits: [{ glAccountId: creditGlId, amount }],
            });
            advance('FINERACT_POSTED', { fineractJournalRef: String(je.resourceId) });
        }
        else {
            advance('FINERACT_POSTED');
        }
        if (cfg.rails.hybx.enabled && req.rail !== 'INTERNAL') {
            const hybx = new hybx_production_1.HybxProductionRail();
            const pay = await hybx.dispatchPayment({
                idempotencyKey: req.idempotencyKey,
                amount: req.amount,
                currency: req.currency,
                creditorIban: req.creditorIban,
                creditorBic: req.creditorBic,
                beneficiaryName: req.beneficiaryName,
                remittanceInfo: req.remittanceInfo,
                rail: req.rail,
            });
            advance('HYBX_RAIL_DISPATCHED', { hybxPaymentId: pay.paymentId });
        }
        else {
            advance('HYBX_RAIL_DISPATCHED');
        }
        const swiftKind = (0, settlement_core_1.swiftKindForRequest)(req);
        const swiftRaw = swiftKind === 'MT102'
            ? (0, swift_1.buildMt102Stub)({
                senderRef: req.idempotencyKey,
                currency: req.currency,
                amount: req.amount,
                valueDate: req.valueDate,
                orderingInstitution: req.orderingName ?? cfg.omnlLegalName,
                beneficiary: req.beneficiaryName,
                iban: req.creditorIban,
                remittance: req.remittanceInfo,
            })
            : (0, swift_1.buildMt103Stub)({
                senderRef: req.idempotencyKey,
                currency: req.currency,
                amount: req.amount,
                valueDate: req.valueDate,
                orderingCustomer: req.orderingName ?? cfg.omnlLegalName,
                beneficiary: req.beneficiaryName,
                iban: req.creditorIban,
                bic: req.creditorBic,
                remittance: req.remittanceInfo,
            });
        if (cfg.rails.swift.enabled) {
            await (0, swift_1.forwardSwiftToListener)(swiftRaw);
        }
        const iso = await (0, omnl_1.archiveIso20022)({
            messageType: swiftKind === 'MT102' ? 'pacs.008' : 'pacs.008',
            direction: 'OUTBOUND',
            raw: swiftRaw,
            settlementRef: record.settlementId,
        });
        advance('ISO20022_ARCHIVED', { iso20022MessageId: iso.messageId });
        if (req.convertToCrypto?.enabled) {
            const balances = await (0, fineract_1.fetchGlBalances)(req.officeId);
            const snap = (0, settlement_core_1.buildMoneySupplySnapshot)(req.officeId, balances);
            const { loadAmount, fullyBacked } = (0, settlement_core_1.m2FiatToTokenLoadAmount)(req.amount, snap.M2.broadMoney);
            if (!fullyBacked) {
                record.errors.push(`M2 backing insufficient for token load (${loadAmount}/${req.amount})`);
            }
            const mint = await (0, omnl_1.requestTokenMint)({
                lineId: req.convertToCrypto.tokenLineId,
                amount: loadAmount,
                recipient: req.convertToCrypto.recipientAddress,
                settlementRef: record.settlementId,
                dryRun: !cfg.production.allowChainMintExecute,
            });
            advance('CHAIN_MINT_REQUESTED', { chainTxHash: mint.txHash });
        }
        else {
            advance('CHAIN_MINT_REQUESTED');
        }
        advance('SETTLED');
        return record;
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        record.errors.push(msg);
        advance('FAILED');
        return record;
    }
}
async function getMoneySupply(officeId) {
    const balances = await (0, fineract_1.fetchGlBalances)(officeId);
    return (0, settlement_core_1.buildMoneySupplySnapshot)(officeId, balances);
}
function getSettlementStatus(id) {
    return settlement_store_1.settlementStore.getById(id);
}
