/**
 * Shared fund ledger: default accounts, POS sale posting, supplier payments,
 * operating expenses, accounting feature flags.
 * RTDB: clients/{clientId}/fundAccounts, fundTransfers, supplierPayments, operatingExpenses
 */

export const SALES_CLEARING_ACCOUNT_ID = 'sales_clearing';
export const OUTFLOW_CLEARING_ACCOUNT_ID = 'outflow_clearing';

export const DEFAULT_FUND_ACCOUNTS = {
    cash: { id: 'cash', name: 'Cash', type: 'cash_drawer', currency: 'BDT' },
    bank: { id: 'bank', name: 'Bank', type: 'bank', currency: 'BDT' },
    mobile: { id: 'mobile', name: 'Mobile banking', type: 'mobile_wallet_merchant', currency: 'BDT' },
    wallet: { id: 'wallet', name: 'Wallet (operating)', type: 'platform_wallet', currency: 'BDT' },
    [SALES_CLEARING_ACCOUNT_ID]: {
        id: SALES_CLEARING_ACCOUNT_ID,
        name: 'POS sales (clearing)',
        type: 'clearing',
        currency: 'BDT'
    },
    [OUTFLOW_CLEARING_ACCOUNT_ID]: {
        id: OUTFLOW_CLEARING_ACCOUNT_ID,
        name: 'Supplier & operating (clearing)',
        type: 'clearing',
        currency: 'BDT'
    }
};

/** Labels for fund account ids (UI + history) */
export const ACCOUNT_LABELS = {
    cash: 'Cash',
    bank: 'Bank',
    mobile: 'Mobile',
    wallet: 'Wallet',
    [SALES_CLEARING_ACCOUNT_ID]: 'Sales clearing',
    [OUTFLOW_CLEARING_ACCOUNT_ID]: 'Outflow clearing'
};

export const EXPENSE_CATEGORIES = ['rent', 'salary', 'utilities', 'logistics', 'marketing', 'misc'];

export function roundMoney(n) {
    const x = Number(n);
    if (Number.isNaN(x)) return 0;
    return Math.round(x * 100) / 100;
}

/** @param {object|null|undefined} rawSiteSettings */
export function readAccountingFeatures(rawSiteSettings) {
    const af =
        rawSiteSettings && typeof rawSiteSettings === 'object' ? rawSiteSettings.accountingFeatures : null;
    const def = {
        posSalePosting: true,
        supplierPayments: true,
        operatingExpenses: true,
        bankReconciliation: true,
        posRefundFundPosting: true,
        netSalesUnified: true,
        arLedger: true,
        periodCloseLock: true,
        settlementReport: true,
        /** Max single POS refund fund movement (0 = no cap) */
        refundMaxPerTx: 0
    };
    if (!af || typeof af !== 'object') return { ...def };
    const refundMax = Number(af.refundMaxPerTx);
    return {
        posSalePosting: af.posSalePosting !== false,
        supplierPayments: af.supplierPayments !== false,
        operatingExpenses: af.operatingExpenses !== false,
        bankReconciliation: af.bankReconciliation !== false,
        posRefundFundPosting: af.posRefundFundPosting !== false,
        netSalesUnified: af.netSalesUnified !== false,
        arLedger: af.arLedger !== false,
        periodCloseLock: af.periodCloseLock !== false,
        settlementReport: af.settlementReport !== false,
        refundMaxPerTx: Number.isFinite(refundMax) && refundMax > 0 ? refundMax : 0
    };
}

/** Local calendar YYYY-MM for period close / recon keys */
export function periodMonthKeyFromMs(ms) {
    const t = Number(ms);
    if (!Number.isFinite(t)) return '';
    const d = new Date(t);
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    return `${y}-${m}`;
}

/**
 * @param {import('firebase/database').Database} db
 * @param {string} clientId
 * @param {{ ref: Function, get: Function, update: Function, serverTimestamp: Function }} api
 */
export async function ensureFundAccounts(db, clientId, { ref, get, update, serverTimestamp }) {
    const base = `clients/${clientId}/fundAccounts`;
    const snap = await get(ref(db, base));
    const patch = {};
    if (!snap.exists()) {
        let w0 = 0;
        try {
            const ws = await get(ref(db, `clients/${clientId}/wallet/currentBalance`));
            w0 = Number(ws.val()) || 0;
        } catch (_) {
            /* ignore */
        }
        for (const [id, def] of Object.entries(DEFAULT_FUND_ACCOUNTS)) {
            const bal = id === 'wallet' ? w0 : 0;
            patch[`${base}/${id}`] = { ...def, balance: bal, updatedAt: serverTimestamp() };
        }
        patch[`${base}/_meta/initializedAt`] = serverTimestamp();
        await update(ref(db), patch);
        return;
    }
    const val = snap.val() || {};
    for (const [id, def] of Object.entries(DEFAULT_FUND_ACCOUNTS)) {
        if (val[id] == null) {
            patch[`${base}/${id}`] = { ...def, balance: 0, updatedAt: serverTimestamp() };
        }
    }
    if (Object.keys(patch).length) await update(ref(db), patch);
}

/**
 * @param {string} method POS breakdown method label
 * @returns {string|null}
 */
export function tenderAccountIdFromPosMethod(method) {
    const m = String(method || '').trim().toLowerCase();
    if (m === 'cash') return 'cash';
    if (m === 'card') return 'bank';
    if (m.includes('mobile')) return 'mobile';
    return null;
}

/**
 * Signed impact on a fund account from a transfer row (+ = into account).
 * @param {object} t
 * @param {string} accountId
 */
export function signedAmountForAccount(t, accountId) {
    if (!t || !accountId) return 0;
    const amt = roundMoney(Number(t.amount) || 0);
    if (amt <= 0) return 0;
    if (t.toAccountId === accountId) return amt;
    if (t.fromAccountId === accountId) return -amt;
    return 0;
}

/**
 * Append POS sale fund movements to client-relative `updates` (same shape as update(clientRef, updates)).
 * @param {Record<string, unknown>} updates
 * @param {import('firebase/database').Database} db
 * @param {string} clientId
 * @param {{ ref: Function, push: Function, increment: Function, serverTimestamp: Function }} api
 * @param {{ orderKey: string, breakdown: Array<{ method?: string, amount?: number }>, uid?: string|null, registerId?: string|null, shiftId?: string|null, clientRequestId?: string|null }} ctx
 * @returns {number} number of transfer lines written
 */
export function appendPosSaleFundPosting(updates, db, clientId, { ref, push, increment, serverTimestamp }, ctx) {
    const { orderKey, breakdown, uid, registerId, shiftId, clientRequestId } = ctx;
    if (!orderKey || !Array.isArray(breakdown) || !breakdown.length) return 0;
    let lines = 0;
    const fromId = SALES_CLEARING_ACCOUNT_ID;
    const ts = Date.now();
    for (const row of breakdown) {
        const amt = roundMoney(Number(row.amount) || 0);
        if (amt <= 0) continue;
        const toId = tenderAccountIdFromPosMethod(row.method);
        if (!toId) continue;
        const tid = push(ref(db, `clients/${clientId}/fundTransfers`)).key;
        if (!tid) continue;
        updates[`fundTransfers/${tid}`] = {
            kind: 'pos_sale',
            fromAccountId: fromId,
            toAccountId: toId,
            amount: amt,
            referenceId: String(orderKey),
            remark: 'POS sale capture',
            orderKey: String(orderKey),
            tenderMethod: row.method || null,
            clientLocalAt: ts,
            createdAtMs: ts,
            createdAt: serverTimestamp(),
            createdByUid: uid || null,
            status: 'posted',
            registerId: registerId || null,
            shiftId: shiftId || null,
            checkoutClientRequestId: clientRequestId || null
        };
        updates[`fundAccounts/${fromId}/balance`] = increment(-amt);
        updates[`fundAccounts/${toId}/balance`] = increment(amt);
        updates[`fundAccounts/${fromId}/updatedAt`] = serverTimestamp();
        updates[`fundAccounts/${toId}/updatedAt`] = serverTimestamp();
        lines += 1;
    }
    return lines;
}

/**
 * Reverse POS sale capture into fund accounts (tender → sales_clearing). Append-only; does not edit prior transfers.
 * @param {Record<string, unknown>} updates
 * @param {import('firebase/database').Database} db
 * @param {string} clientId
 * @param {{ ref: Function, push: Function, increment: Function, serverTimestamp: Function }} api
 * @param {{ orderKey: string, breakdown: Array<{ method?: string, amount?: number }>, uid?: string|null, shiftId?: string|null, refundLedgerIndex?: number }} ctx
 * @returns {number}
 */
export function appendPosRefundFundPosting(updates, db, clientId, { ref, push, increment, serverTimestamp }, ctx) {
    const { orderKey, breakdown, uid, shiftId, refundLedgerIndex } = ctx;
    if (!orderKey || !Array.isArray(breakdown) || !breakdown.length) return 0;
    const toId = SALES_CLEARING_ACCOUNT_ID;
    let lines = 0;
    const ts = Date.now();
    for (const row of breakdown) {
        const amt = roundMoney(Number(row.amount) || 0);
        if (amt <= 0) continue;
        const fromId = tenderAccountIdFromPosMethod(row.method);
        if (!fromId) continue;
        const tid = push(ref(db, `clients/${clientId}/fundTransfers`)).key;
        if (!tid) continue;
        updates[`fundTransfers/${tid}`] = {
            kind: 'pos_refund',
            fromAccountId: fromId,
            toAccountId: toId,
            amount: amt,
            referenceId: String(orderKey),
            remark: 'POS refund reversal',
            orderKey: String(orderKey),
            tenderMethod: row.method || null,
            refundLedgerIndex: refundLedgerIndex != null ? Number(refundLedgerIndex) : null,
            clientLocalAt: ts,
            createdAtMs: ts,
            createdAt: serverTimestamp(),
            createdByUid: uid || null,
            status: 'posted',
            shiftId: shiftId || null
        };
        updates[`fundAccounts/${fromId}/balance`] = increment(-amt);
        updates[`fundAccounts/${toId}/balance`] = increment(amt);
        updates[`fundAccounts/${fromId}/updatedAt`] = serverTimestamp();
        updates[`fundAccounts/${toId}/updatedAt`] = serverTimestamp();
        lines += 1;
    }
    return lines;
}

/**
 * @param {import('firebase/database').Database} db
 * @param {string} clientId
 * @param {{ ref: Function, push: Function, increment: Function, serverTimestamp: Function }} api
 * @param {{ amount: number, distributorId: string, paidFromAccountId: string, purchaseId?: string|null, note?: string|null, uid?: string|null }} body
 */
export function buildSupplierPaymentRootUpdates(db, clientId, { ref, push, increment, serverTimestamp }, body) {
    const amount = roundMoney(Number(body.amount) || 0);
    const distributorId = String(body.distributorId || '').trim();
    const fromId = String(body.paidFromAccountId || 'cash').trim();
    if (!distributorId || amount <= 0) throw new Error('invalid_supplier_payment');
    if ([SALES_CLEARING_ACCOUNT_ID, OUTFLOW_CLEARING_ACCOUNT_ID].includes(fromId)) {
        throw new Error('invalid_pay_account');
    }
    const paymentId = push(ref(db, `clients/${clientId}/supplierPayments`)).key;
    if (!paymentId) throw new Error('payment_key');
    const tid = push(ref(db, `clients/${clientId}/fundTransfers`)).key;
    if (!tid) throw new Error('transfer_key');
    const base = `clients/${clientId}`;
    const ts = Date.now();
    const uid = body.uid || null;
    const purchaseId = body.purchaseId ? String(body.purchaseId) : null;
    const note = body.note != null ? String(body.note).trim().slice(0, 500) : '';
    const rootUpdates = {
        [`${base}/fundTransfers/${tid}`]: {
            kind: 'supplier_payment',
            fromAccountId: fromId,
            toAccountId: OUTFLOW_CLEARING_ACCOUNT_ID,
            amount,
            referenceId: paymentId,
            remark: note || null,
            purchaseId,
            distributorId,
            clientLocalAt: ts,
            createdAtMs: ts,
            createdAt: serverTimestamp(),
            createdByUid: uid,
            status: 'posted'
        },
        [`${base}/fundAccounts/${fromId}/balance`]: increment(-amount),
        [`${base}/fundAccounts/${OUTFLOW_CLEARING_ACCOUNT_ID}/balance`]: increment(amount),
        [`${base}/fundAccounts/${fromId}/updatedAt`]: serverTimestamp(),
        [`${base}/fundAccounts/${OUTFLOW_CLEARING_ACCOUNT_ID}/updatedAt`]: serverTimestamp(),
        [`${base}/supplierPayments/${paymentId}`]: {
            amount,
            distributorId,
            paidFromAccountId: fromId,
            purchaseId,
            note: note || null,
            createdAt: serverTimestamp(),
            createdAtMs: ts,
            createdByUid: uid,
            allowAdvance: !!body.allowAdvance
        },
        [`${base}/distributors/${distributorId}/dueBalance`]: increment(-amount)
    };
    return { rootUpdates, paymentId, amount, transferId: tid };
}

/**
 * @param {import('firebase/database').Database} db
 * @param {string} clientId
 * @param {{ ref: Function, push: Function, increment: Function, serverTimestamp: Function }} api
 * @param {{ amount: number, category: string, paidFromAccountId: string, expenseDate?: string|null, notes?: string|null, uid?: string|null }} body
 */
export function buildOperatingExpenseRootUpdates(db, clientId, { ref, push, increment, serverTimestamp }, body) {
    const amount = roundMoney(Number(body.amount) || 0);
    const category = String(body.category || 'misc').trim().slice(0, 64);
    const fromId = String(body.paidFromAccountId || 'cash').trim();
    if (amount <= 0) throw new Error('invalid_expense');
    if ([SALES_CLEARING_ACCOUNT_ID, OUTFLOW_CLEARING_ACCOUNT_ID].includes(fromId)) {
        throw new Error('invalid_expense_account');
    }
    const expenseId = push(ref(db, `clients/${clientId}/operatingExpenses`)).key;
    if (!expenseId) throw new Error('expense_key');
    const tid = push(ref(db, `clients/${clientId}/fundTransfers`)).key;
    if (!tid) throw new Error('transfer_key');
    const base = `clients/${clientId}`;
    const ts = Date.now();
    const uid = body.uid || null;
    const notes = body.notes != null ? String(body.notes).trim().slice(0, 500) : '';
    const expenseDate = body.expenseDate != null ? String(body.expenseDate).trim().slice(0, 32) : null;
    const rootUpdates = {
        [`${base}/fundTransfers/${tid}`]: {
            kind: 'operating_expense',
            fromAccountId: fromId,
            toAccountId: OUTFLOW_CLEARING_ACCOUNT_ID,
            amount,
            referenceId: expenseId,
            remark: notes || category,
            category,
            clientLocalAt: ts,
            createdAtMs: ts,
            createdAt: serverTimestamp(),
            createdByUid: uid,
            status: 'posted'
        },
        [`${base}/fundAccounts/${fromId}/balance`]: increment(-amount),
        [`${base}/fundAccounts/${OUTFLOW_CLEARING_ACCOUNT_ID}/balance`]: increment(amount),
        [`${base}/fundAccounts/${fromId}/updatedAt`]: serverTimestamp(),
        [`${base}/fundAccounts/${OUTFLOW_CLEARING_ACCOUNT_ID}/updatedAt`]: serverTimestamp(),
        [`${base}/operatingExpenses/${expenseId}`]: {
            amount,
            category,
            paidFromAccountId: fromId,
            expenseDate: expenseDate || null,
            notes: notes || null,
            fundTransferId: tid,
            createdAt: serverTimestamp(),
            createdAtMs: ts,
            createdByUid: uid
        }
    };
    return { rootUpdates, expenseId, amount, transferId: tid };
}
