import { initializeApp } from "https://www.gstatic.com/firebasejs/11.9.0/firebase-app.js";
import {
    getDatabase,
    ref,
    get,
    onValue,
    update,
    push,
    increment,
    serverTimestamp,
    query,
    orderByChild,
    limitToLast
} from "https://www.gstatic.com/firebasejs/11.9.0/firebase-database.js";
import { getAuth, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.9.0/firebase-auth.js";
import { firebaseConfig } from './firebase-config.js';
import { staffHasStrictPanelPermission } from './staff-page-guard.js';
import {
    computeBillTotalsFiltered,
    sumVatFiltered,
    sumRefundLedgerFiltered,
    computeChannelTotalsFiltered,
    computeNetBillTotalsFiltered,
    computeChannelNetTotalsFiltered
} from './finance-analytics-v5.js';
import { computeExpectedTenderByMethod } from './pos-finance.js';
import { getOrderCreatedAtMs, classifyOrderChannel } from './order-time-channel.js';
import {
    ensureFundAccounts,
    ACCOUNT_LABELS,
    readAccountingFeatures,
    buildOperatingExpenseRootUpdates,
    EXPENSE_CATEGORIES,
    signedAmountForAccount,
    roundMoney,
    periodMonthKeyFromMs
} from './fund-ledger.js';
import { recordRtdbTelemetry } from './rtdb-telemetry.js';

/** Max fundTransfers rows synced via ordered query (recon page uses full tree). */
const FUND_TRANSFERS_QUERY_LIMIT = 3000;

/** @type {'hub'|'transfer'|'opex'|'recon'|'settlement'|'ar'} */
let ACC_PAGE = 'hub';

let WIRED_RANGE_DELEGATE = false;
let WIRED_FEATURE_FORM = false;
let WIRED_TRANSFER_FORM = false;
let WIRED_HISTORY_FILTERS = false;
let WIRED_OPEX_FORM = false;
let WIRED_BANK_RECON = false;
let WIRED_AR_PANEL = false;
let HASH_ROUTING_INSTALLED = false;

const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
const auth = getAuth(app);

let CURRENT_CLIENT_ID = '';
/** @type {object[]} */
let ordersCache = [];
let rangePreset = '7d';
let unsubOrders = null;
let unsubWallet = null;
let unsubFundAccounts = null;
let unsubTransfers = null;
/** @type {Record<string, object>} */
let fundAccountsCache = {};
let platformWalletBalance = 0;
/** @type {Array<{ id: string } & object>} */
let transfersList = [];
let historyFilter = 'all';
let transferSubmitBusy = false;
let siteSettingsCacheAcc = {};
let unsubOpex = null;
let unsubDistributors = null;
let unsubReceivable = null;
let unsubDashSummary = null;
/** @type {Record<string, object>} */
let receivablesCache = {};
let brSnapCache = null;
let brSnapKey = '';
/** @type {Record<string, object>} */
let operatingExpensesVal = {};
/** @type {Array<{ id: string } & object>} */
let brRows = [];
/** @type {Set<string>} */
let brCleared = new Set();

function money(n) {
    const x = Number(n) || 0;
    return '৳' + x.toLocaleString('bn-BD', { maximumFractionDigits: 2 });
}

function plainMoney(n) {
    const x = Number(n) || 0;
    return x.toLocaleString('bn-BD', { maximumFractionDigits: 2 });
}

function escHtml(s) {
    const d = document.createElement('div');
    d.textContent = s == null ? '' : String(s);
    return d.innerHTML;
}

function showToast(msg, ok = true) {
    const t = document.getElementById('toast');
    const m = document.getElementById('toast-message');
    const i = document.getElementById('toast-icon');
    m.textContent = msg;
    t.classList.remove('pur-toast--success', 'pur-toast--error', 'is-hidden');
    t.classList.add(ok ? 'pur-toast--success' : 'pur-toast--error', 'is-visible');
    i.className = (ok ? 'fa-solid fa-circle-check text-emerald-400' : 'fa-solid fa-circle-exclamation text-red-400') + ' mt-0.5';
    clearTimeout(showToast._h);
    showToast._h = setTimeout(() => {
        t.classList.remove('is-visible');
        t.classList.add('is-hidden');
    }, 3800);
}

/** Bangla short message for Firebase init failures (RTDB). */
function formatFirebaseInitError(e) {
    const code = e && typeof e === 'object' && e.code ? String(e.code) : '';
    const msg = e && typeof e === 'object' && e.message ? String(e.message) : String(e || '');
    if (code === 'permission-denied') return 'ডাটাবেজ অনুমতি নেই (permission-denied)।';
    if (code === 'unavailable') return 'সার্ভার অস্থায়ী অপ্রাপ্য—কিছুক্ষণ পর চেষ্টা করুন।';
    if (code === 'network-request-failed') return 'নেটওয়ার্ক ত্রুটি—কানেকশন চেক করুন।';
    if (msg) return `লোড ত্রুটি: ${msg.slice(0, 140)}`;
    return 'লোড ব্যর্থ।';
}

function logInitStage(stage, e) {
    console.error(`[Accounting init] ${stage}`, e);
}

function getRangeMs(preset) {
    const now = new Date();
    const endD = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
    const endMs = endD.getTime();
    const startD = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    if (preset === 'today') {
        /* start is start of today */
    } else if (preset === '7d') {
        startD.setDate(startD.getDate() - 6);
    } else if (preset === '30d') {
        startD.setDate(startD.getDate() - 29);
    }
    const startMs = new Date(startD.getFullYear(), startD.getMonth(), startD.getDate()).getTime();
    return { startMs, endMs };
}

function formatRangeLabel(preset) {
    const { startMs, endMs } = getRangeMs(preset);
    const o = { dateStyle: 'short' };
    return `${new Date(startMs).toLocaleDateString('bn-BD', o)} — ${new Date(endMs).toLocaleDateString('bn-BD', o)}`;
}

function setAccRangeLabelText() {
    const txt = formatRangeLabel(rangePreset);
    document.querySelectorAll('[data-acc-range-label]').forEach((el) => {
        el.textContent = txt;
    });
}

function refreshRangeCards() {
    setAccRangeLabelText();
    if (!document.getElementById('acc-sales')) {
        refreshSettlementReadout();
        refreshAccountingLedgerSummaries();
        return;
    }
    const { startMs, endMs } = getRangeMs(rangePreset);
    const featAcc = readAccountingFeatures(siteSettingsCacheAcc);
    const bill = computeBillTotalsFiltered(ordersCache, 'all', startMs, endMs);
    const netB = computeNetBillTotalsFiltered(ordersCache, 'all', startMs, endMs);
    const vat = sumVatFiltered(ordersCache, 'all', startMs, endMs);
    const ref = sumRefundLedgerFiltered(ordersCache, 'all', startMs, endMs);
    const chGross = computeChannelTotalsFiltered(ordersCache, startMs, endMs);
    const chNet = featAcc.netSalesUnified
        ? computeChannelNetTotalsFiltered(ordersCache, startMs, endMs)
        : null;

    if (featAcc.netSalesUnified) {
        document.getElementById('acc-sales').textContent = money(netB.netBillSum);
        const gl = document.getElementById('acc-sales-gross-line');
        const gv = document.getElementById('acc-sales-gross-val');
        if (gv) gv.textContent = money(bill.billSum);
        if (gl) gl.classList.remove('hidden');
        document.getElementById('acc-ch-pos').textContent =
            money(chNet.posNet) + ` (${chNet.posOrders})`;
        document.getElementById('acc-ch-online').textContent =
            money(chNet.onlineNet) + ` (${chNet.onlineOrders})`;
        document.getElementById('acc-ch-unknown').textContent =
            money(chNet.unknownNet) + ` (${chNet.unknownOrders})`;
    } else {
        document.getElementById('acc-sales').textContent = money(bill.billSum);
        document.getElementById('acc-sales-gross-line')?.classList.add('hidden');
        document.getElementById('acc-ch-pos').textContent =
            money(chGross.posSales) + ` (${chGross.posOrders})`;
        document.getElementById('acc-ch-online').textContent =
            money(chGross.onlineSales) + ` (${chGross.onlineOrders})`;
        document.getElementById('acc-ch-unknown').textContent =
            money(chGross.unknownSales) + ` (${chGross.unknownOrders})`;
    }
    document.getElementById('acc-orders-count').textContent = `অর্ডার (রেঞ্জে): ${bill.orderCount}`;
    document.getElementById('acc-vat').textContent = money(vat);
    document.getElementById('acc-refunds').textContent = money(ref);
    refreshAccountingLedgerSummaries();
}

function sumSupplierDue(distVal) {
    if (!distVal || typeof distVal !== 'object') return 0;
    let s = 0;
    for (const k of Object.keys(distVal)) {
        const d = distVal[k];
        if (d && typeof d === 'object') s += Number(d.dueBalance) || 0;
    }
    return Math.round(s * 100) / 100;
}

function fundBalance(id) {
    const row = fundAccountsCache[id];
    if (row && typeof row.balance === 'number' && !Number.isNaN(row.balance)) return row.balance;
    if (row && row.balance != null) return Number(row.balance) || 0;
    return null;
}

function refreshFundSummaryCards() {
    const set = (id, val) => {
        const el = document.getElementById(id);
        if (el) el.textContent = val == null ? '—' : money(Number(val) || 0);
    };
    set('acc-fund-cash', fundBalance('cash'));
    set('acc-fund-bank', fundBalance('bank'));
    set('acc-fund-mobile', fundBalance('mobile'));
    const fw = fundBalance('wallet');
    set('acc-fund-wallet', fw != null ? fw : 0);
    const pw = document.getElementById('acc-platform-wallet');
    if (pw) pw.textContent = money(platformWalletBalance);
}

function syncTransferPreviewBalances() {
    const fromId = document.getElementById('tf-from')?.value || 'cash';
    const toId = document.getElementById('tf-to')?.value || 'bank';
    const fromB = fundBalance(fromId);
    const toB = fundBalance(toId);
    const fromEl = document.getElementById('tf-from-balance');
    const toEl = document.getElementById('tf-to-balance');
    if (fromEl) fromEl.value = plainMoney(fromB != null ? fromB : 0);
    if (toEl) toEl.value = plainMoney(toB != null ? toB : 0);
}

function renderHistory() {
    const body = document.getElementById('tf-history-body');
    const empty = document.getElementById('tf-history-empty');
    if (!body) return;
    const filt = historyFilter;
    const rows = transfersList.filter((t) => {
        if (filt === 'all') return true;
        return t.fromAccountId === filt || t.toAccountId === filt;
    });
    if (!rows.length) {
        body.innerHTML = '';
        empty?.classList.remove('hidden');
        return;
    }
    empty?.classList.add('hidden');
    body.innerHTML = rows
        .map((t) => {
            const when = t.clientLocalAt
                ? new Date(t.clientLocalAt).toLocaleString('bn-BD', { dateStyle: 'short', timeStyle: 'short' })
                : '—';
            const fromL = ACCOUNT_LABELS[t.fromAccountId] || t.fromAccountId;
            const toL = ACCOUNT_LABELS[t.toAccountId] || t.toAccountId;
            const kind = t.kind ? String(t.kind) : 'manual';
            return `<tr class="hover:bg-zinc-900/50">
                <td class="px-3 py-2 whitespace-nowrap text-zinc-400">${escHtml(when)}</td>
                <td class="px-3 py-2 text-xs text-zinc-500">${escHtml(kind)}</td>
                <td class="px-3 py-2">${escHtml(fromL)}</td>
                <td class="px-3 py-2">${escHtml(toL)}</td>
                <td class="px-3 py-2 text-right font-bold text-emerald-300 tabular-nums">${escHtml(money(t.amount))}</td>
                <td class="px-3 py-2 max-w-[140px] truncate" title="${escHtml(t.referenceId || '')}">${escHtml(t.referenceId || '—')}</td>
                <td class="px-3 py-2 max-w-[220px] truncate text-zinc-400" title="${escHtml(t.remark || '')}">${escHtml(t.remark || '—')}</td>
            </tr>`;
        })
        .join('');
}

function refreshAccountingLedgerSummaries() {
    const { startMs, endMs } = getRangeMs(rangePreset);
    let posL = 0;
    let posRf = 0;
    let sup = 0;
    let opex = 0;
    for (const t of transfersList) {
        const ms = Number(t.clientLocalAt) || Number(t.createdAtMs) || 0;
        if (ms < startMs || ms > endMs) continue;
        const k = String(t.kind || '');
        const amt = roundMoney(Number(t.amount) || 0);
        if (amt <= 0) continue;
        if (k === 'pos_sale') posL += amt;
        else if (k === 'pos_refund') posRf += amt;
        else if (k === 'supplier_payment') sup += amt;
        else if (k === 'operating_expense') opex += amt;
    }
    const posNet = roundMoney(posL - posRf);
    const elp = document.getElementById('acc-ledger-pos-sales');
    const els = document.getElementById('acc-ledger-supplier');
    const elo = document.getElementById('acc-ledger-opex');
    if (elp) elp.textContent = money(posNet);
    if (els) els.textContent = money(sup);
    if (elo) elo.textContent = money(opex);
    renderOxList();
    void refreshReconVarianceBadge();
    refreshSettlementReadout();
}

function refreshSettlementReadout() {
    const host = document.getElementById('acc-settlement-section');
    if (!host || !readAccountingFeatures(siteSettingsCacheAcc).settlementReport) return;
    const { startMs, endMs } = getRangeMs(rangePreset);
    const om = {};
    (ordersCache || []).forEach((o, i) => {
        om[`o${i}`] = o;
    });
    const exp = computeExpectedTenderByMethod(
        om,
        startMs,
        endMs,
        (o) => classifyOrderChannel(o) === 'pos',
        (o) => getOrderCreatedAtMs(o)
    );
    let cashSale = 0;
    let cashRef = 0;
    let bankSale = 0;
    let bankRef = 0;
    let mobSale = 0;
    let mobRef = 0;
    for (const t of transfersList) {
        const ms = Number(t.clientLocalAt) || Number(t.createdAtMs) || 0;
        if (ms < startMs || ms > endMs) continue;
        const k = String(t.kind || '');
        const amt = roundMoney(Number(t.amount) || 0);
        if (amt <= 0) continue;
        if (k === 'pos_sale') {
            if (t.toAccountId === 'cash') cashSale += amt;
            if (t.toAccountId === 'bank') bankSale += amt;
            if (t.toAccountId === 'mobile') mobSale += amt;
        } else if (k === 'pos_refund') {
            if (t.fromAccountId === 'cash') cashRef += amt;
            if (t.fromAccountId === 'bank') bankRef += amt;
            if (t.fromAccountId === 'mobile') mobRef += amt;
        }
    }
    const set3 = (expC, fundN, dEl, expEl, fundEl) => {
        if (expEl) expEl.textContent = money(expC);
        if (fundEl) fundEl.textContent = money(fundN);
        if (dEl) dEl.textContent = money(roundMoney(expC - fundN));
    };
    set3(exp.cash, roundMoney(cashSale - cashRef), document.getElementById('st-delta-cash'), document.getElementById('st-exp-cash'), document.getElementById('st-fund-cash'));
    set3(exp.card, roundMoney(bankSale - bankRef), document.getElementById('st-delta-card'), document.getElementById('st-exp-card'), document.getElementById('st-fund-card'));
    set3(exp.mobile, roundMoney(mobSale - mobRef), document.getElementById('st-delta-mobile'), document.getElementById('st-exp-mobile'), document.getElementById('st-fund-mobile'));
}

function renderArList() {
    const body = document.getElementById('ar-list-body');
    const empty = document.getElementById('ar-list-empty');
    const sel = document.getElementById('ar-recv-select');
    if (!body || !sel) return;
    const rows = [];
    sel.innerHTML = '<option value="">— Select receivable —</option>';
    for (const [id, row] of Object.entries(receivablesCache || {})) {
        if (!row || id === '_meta') continue;
        const open = roundMoney(Number(row.amountOpen) || 0);
        rows.push(
            `<tr><td class="py-2">${escHtml(String(row.label || ''))}</td><td class="py-2 font-mono text-xs">${escHtml(
                String(row.orderKey || '—')
            )}</td><td class="py-2 text-right font-semibold">${escHtml(money(open))}</td></tr>`
        );
        if (open > 0.005) {
            const opt = document.createElement('option');
            opt.value = id;
            opt.textContent = `${row.label || id} · ${money(open)}`;
            sel.appendChild(opt);
        }
    }
    body.innerHTML = rows.slice(0, 80).join('');
    if (empty) empty.classList.toggle('hidden', rows.length > 0);
}

function wireArPanel() {
    if (WIRED_AR_PANEL) return;
    if (!document.getElementById('ar-open-btn')) return;
    WIRED_AR_PANEL = true;
    document.getElementById('ar-open-btn')?.addEventListener('click', async () => {
        if (!readAccountingFeatures(siteSettingsCacheAcc).arLedger) {
            showToast('AR বন্ধ আছে।', false);
            return;
        }
        if (!(await assertMonthOpenForPosting(Date.now()))) return;
        const user = auth.currentUser;
        if (!user || !CURRENT_CLIENT_ID) return;
        const label = String(document.getElementById('ar-label')?.value || '').trim();
        const orderKey = String(document.getElementById('ar-order-key')?.value || '').trim();
        const amt = roundMoney(parseFloat(document.getElementById('ar-open-amt')?.value || '0') || 0);
        if (!label || amt <= 0) {
            showToast('লেবেল ও amount দিন।', false);
            return;
        }
        const rid = push(ref(db, `clients/${CURRENT_CLIENT_ID}/customersReceivable`)).key;
        if (!rid) return;
        try {
            await update(ref(db), {
                [`clients/${CURRENT_CLIENT_ID}/customersReceivable/${rid}`]: {
                    label,
                    orderKey: orderKey || null,
                    amountOpen: amt,
                    createdAtMs: Date.now(),
                    createdByUid: user.uid
                }
            });
            showToast('AR খোলা হয়েছে।', true);
            document.getElementById('ar-label').value = '';
            document.getElementById('ar-order-key').value = '';
            document.getElementById('ar-open-amt').value = '';
        } catch (err) {
            console.error(err);
            showToast('সেভ ব্যর্থ।', false);
        }
    });
    document.getElementById('ar-collect-btn')?.addEventListener('click', async () => {
        if (!readAccountingFeatures(siteSettingsCacheAcc).arLedger) {
            showToast('AR বন্ধ।', false);
            return;
        }
        if (!(await assertMonthOpenForPosting(Date.now()))) return;
        const user = auth.currentUser;
        if (!user || !CURRENT_CLIENT_ID) return;
        const rid = document.getElementById('ar-recv-select')?.value || '';
        const amt = roundMoney(parseFloat(document.getElementById('ar-collect-amt')?.value || '0') || 0);
        const acct = document.getElementById('ar-collect-acct')?.value || 'cash';
        if (!rid || amt <= 0) {
            showToast('Receivable ও amount নির্বাচন করুন।', false);
            return;
        }
        const row = receivablesCache[rid];
        const open0 = roundMoney(Number(row?.amountOpen) || 0);
        if (amt > open0 + 1e-6) {
            showToast('Amount খোলা বাকির চেয়ে বেশি।', false);
            return;
        }
        try {
            const cid = push(
                ref(db, `clients/${CURRENT_CLIENT_ID}/customersReceivable/${rid}/collections`)
            ).key;
            if (!cid) throw new Error('no collection key');
            await update(ref(db), {
                [`clients/${CURRENT_CLIENT_ID}/customersReceivable/${rid}/amountOpen`]: increment(-amt),
                [`clients/${CURRENT_CLIENT_ID}/customersReceivable/${rid}/collections/${cid}`]: {
                    amount: amt,
                    paidIntoAccountId: acct,
                    createdAtMs: Date.now(),
                    createdByUid: user.uid
                },
                [`clients/${CURRENT_CLIENT_ID}/fundAccounts/${acct}/balance`]: increment(amt),
                [`clients/${CURRENT_CLIENT_ID}/fundAccounts/${acct}/updatedAt`]: serverTimestamp()
            });
            showToast('সংগ্রহ রেকর্ড হয়েছে।', true);
            document.getElementById('ar-collect-amt').value = '';
        } catch (err) {
            console.error(err);
            showToast('সেভ ব্যর্থ।', false);
        }
    });
}

function applyAccountingFeatureVisibility() {
    const feat = readAccountingFeatures(siteSettingsCacheAcc);
    const opex = document.getElementById('acc-opex-section');
    const recon = document.getElementById('acc-recon-section');
    const ar = document.getElementById('acc-ar-section');
    const st = document.getElementById('acc-settlement-section');
    if (opex) opex.classList.toggle('hidden', !feat.operatingExpenses);
    if (recon) recon.classList.toggle('hidden', !feat.bankReconciliation);
    if (ar) ar.classList.toggle('hidden', !feat.arLedger);
    if (st) st.classList.toggle('hidden', !feat.settlementReport);
}

async function assertMonthOpenForPosting(tsMs) {
    if (!readAccountingFeatures(siteSettingsCacheAcc).periodCloseLock || !CURRENT_CLIENT_ID) return true;
    const mk = periodMonthKeyFromMs(tsMs);
    if (!mk) return true;
    const cm = await get(ref(db, `clients/${CURRENT_CLIENT_ID}/accountingSettings/closedMonths/${mk}`));
    if (cm.exists() && cm.val()) {
        showToast('এই মাসের জন্য পিরিয়ড লক করা — পোস্টিং বন্ধ।', false);
        return false;
    }
    return true;
}

async function loadAccountingSettingsIntoCache() {
    if (!CURRENT_CLIENT_ID) return;
    const ssSnap = await get(ref(db, `clients/${CURRENT_CLIENT_ID}/siteSettings`));
    siteSettingsCacheAcc = ssSnap.val() || {};
}

async function loadAccountingSettingsUi() {
    await loadAccountingSettingsIntoCache();
    const feat = readAccountingFeatures(siteSettingsCacheAcc);
    const p = document.getElementById('af-pos');
    const pr = document.getElementById('af-pos-refund');
    const n = document.getElementById('af-net');
    const s = document.getElementById('af-supplier');
    const o = document.getElementById('af-opex');
    const r = document.getElementById('af-recon');
    const ar = document.getElementById('af-ar');
    const per = document.getElementById('af-period');
    const st = document.getElementById('af-settle');
    const rm = document.getElementById('af-refund-max');
    if (p) p.checked = feat.posSalePosting;
    if (pr) pr.checked = feat.posRefundFundPosting;
    if (n) n.checked = feat.netSalesUnified;
    if (s) s.checked = feat.supplierPayments;
    if (o) o.checked = feat.operatingExpenses;
    if (r) r.checked = feat.bankReconciliation;
    if (ar) ar.checked = feat.arLedger;
    if (per) per.checked = feat.periodCloseLock;
    if (st) st.checked = feat.settlementReport;
    if (rm) rm.value = String(feat.refundMaxPerTx > 0 ? feat.refundMaxPerTx : 0);
    applyAccountingFeatureVisibility();
}

function wireAccountingFeatureForm() {
    if (WIRED_FEATURE_FORM) return;
    if (!document.getElementById('af-save')) return;
    WIRED_FEATURE_FORM = true;
    document.getElementById('af-save')?.addEventListener('click', async () => {
        if (!CURRENT_CLIENT_ID) return;
        const refundMaxRaw = parseFloat(document.getElementById('af-refund-max')?.value || '0') || 0;
        const payload = {
            posSalePosting: !!document.getElementById('af-pos')?.checked,
            posRefundFundPosting: !!document.getElementById('af-pos-refund')?.checked,
            netSalesUnified: !!document.getElementById('af-net')?.checked,
            supplierPayments: !!document.getElementById('af-supplier')?.checked,
            operatingExpenses: !!document.getElementById('af-opex')?.checked,
            bankReconciliation: !!document.getElementById('af-recon')?.checked,
            arLedger: !!document.getElementById('af-ar')?.checked,
            periodCloseLock: !!document.getElementById('af-period')?.checked,
            settlementReport: !!document.getElementById('af-settle')?.checked,
            refundMaxPerTx: refundMaxRaw > 0 ? Math.round(refundMaxRaw * 100) / 100 : 0
        };
        try {
            await update(ref(db), {
                [`clients/${CURRENT_CLIENT_ID}/siteSettings/accountingFeatures`]: payload
            });
            siteSettingsCacheAcc = { ...siteSettingsCacheAcc, accountingFeatures: payload };
            applyAccountingFeatureVisibility();
            showToast('ফ্ল্যাগ সেভ হয়েছে।', true);
        } catch (err) {
            console.error(err);
            showToast(err?.message || 'সেভ ব্যর্থ।', false);
        }
    });
}

function fillOxCategorySelect() {
    const sel = document.getElementById('ox-cat');
    if (!sel) return;
    const opts = [...EXPENSE_CATEGORIES, 'custom'].map(
        (c) => `<option value="${c}">${escHtml(c)}</option>`
    );
    sel.innerHTML = opts.join('');
}

function renderOxList() {
    const body = document.getElementById('ox-list-body');
    const empty = document.getElementById('ox-list-empty');
    const totEl = document.getElementById('ox-range-total');
    if (!body) return;
    const { startMs, endMs } = getRangeMs(rangePreset);
    const items = [];
    let sum = 0;
    for (const [id, row] of Object.entries(operatingExpensesVal || {})) {
        if (!row || id === '_meta') continue;
        const ms = Number(row.createdAtMs) || 0;
        if (ms < startMs || ms > endMs) continue;
        const amt = roundMoney(Number(row.amount) || 0);
        sum += amt;
        const when = ms
            ? new Date(ms).toLocaleString('bn-BD', { dateStyle: 'short', timeStyle: 'short' })
            : '—';
        items.push({ ms, html: `<tr><td class="px-2 py-1.5 text-zinc-400">${escHtml(when)}</td><td class="px-2 py-1.5">${escHtml(
            String(row.category || '')
        )}</td><td class="px-2 py-1.5 text-right font-semibold tabular-nums">${escHtml(money(amt))}</td></tr>` });
    }
    items.sort((a, b) => b.ms - a.ms);
    body.innerHTML = items
        .slice(0, 80)
        .map((x) => x.html)
        .join('');
    if (totEl) totEl.textContent = money(sum);
    if (empty) empty.classList.toggle('hidden', items.length > 0);
}

function wireOperatingExpenseForm() {
    if (WIRED_OPEX_FORM) return;
    if (!document.getElementById('ox-submit')) return;
    WIRED_OPEX_FORM = true;
    fillOxCategorySelect();
    const cat = document.getElementById('ox-cat');
    const cust = document.getElementById('ox-cat-custom');
    cat?.addEventListener('change', () => {
        const isC = cat.value === 'custom';
        cust?.classList.toggle('hidden', !isC);
    });
    const d0 = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    const oxDate = document.getElementById('ox-date');
    if (oxDate && !oxDate.value) oxDate.value = `${d0.getFullYear()}-${pad(d0.getMonth() + 1)}-${pad(d0.getDate())}`;

    document.getElementById('ox-submit')?.addEventListener('click', async () => {
        if (!readAccountingFeatures(siteSettingsCacheAcc).operatingExpenses) {
            showToast('Operating expenses বন্ধ আছে (ফ্ল্যাগ)।', false);
            return;
        }
        const user = auth.currentUser;
        if (!user || !CURRENT_CLIENT_ID) return;
        const amount = roundMoney(parseFloat(document.getElementById('ox-amount')?.value || '0') || 0);
        const fromId = document.getElementById('ox-from')?.value || 'cash';
        let category = String(document.getElementById('ox-cat')?.value || 'misc');
        if (category === 'custom') {
            category = String(document.getElementById('ox-cat-custom')?.value || '').trim() || 'misc';
        }
        const expenseDate = document.getElementById('ox-date')?.value || null;
        const notes = String(document.getElementById('ox-notes')?.value || '').trim();
        if (amount <= 0) {
            showToast('সঠিক amount দিন।', false);
            return;
        }
        const accSnap = await get(ref(db, `clients/${CURRENT_CLIENT_ID}/fundAccounts`));
        const accVal = accSnap.val() || {};
        const fromBal = Number(accVal[fromId]?.balance) || 0;
        if (fromBal < amount) {
            showToast('নির্বাচিত অ্যাকাউন্টে পর্যাপ্ত ব্যালান্স নেই।', false);
            return;
        }
        let oxTsMs = Date.now();
        if (expenseDate && /^\d{4}-\d{2}-\d{2}$/.test(String(expenseDate))) {
            const p = String(expenseDate).split('-').map(Number);
            oxTsMs = new Date(p[0], p[1] - 1, p[2], 12, 0, 0, 0).getTime();
        }
        if (!(await assertMonthOpenForPosting(oxTsMs))) return;
        try {
            const { rootUpdates } = buildOperatingExpenseRootUpdates(
                db,
                CURRENT_CLIENT_ID,
                { ref, push, increment, serverTimestamp },
                { amount, category, paidFromAccountId: fromId, expenseDate, notes, uid: user.uid }
            );
            await update(ref(db), rootUpdates);
            showToast('খরচ সেভ হয়েছে।', true);
            document.getElementById('ox-amount').value = '';
            document.getElementById('ox-notes').value = '';
        } catch (err) {
            console.error(err);
            showToast(err?.message || 'সেভ ব্যর্থ।', false);
        }
    });
}

function monthBoundsFromPeriodKey(pk) {
    const m = /^(\d{4})-(\d{2})$/.exec(String(pk || '').trim());
    if (!m) return { startMs: 0, endMs: 0 };
    const y = Number(m[1]);
    const mo = Number(m[2]) - 1;
    const startMs = new Date(y, mo, 1).getTime();
    const endMs = new Date(y, mo + 1, 0, 23, 59, 59, 999).getTime();
    return { startMs, endMs };
}

function renderBrTable(accountId) {
    const body = document.getElementById('br-body');
    if (!body) return;
    const pk = document.getElementById('br-period')?.value || '';
    const { startMs, endMs } = monthBoundsFromPeriodKey(pk);
    const rows = transfersList.filter((t) => {
        const ms = Number(t.clientLocalAt) || Number(t.createdAtMs) || 0;
        if (ms < startMs || ms > endMs) return false;
        return t.fromAccountId === accountId || t.toAccountId === accountId;
    });
    brRows = rows;
    const clearedSet = new Set();
    if (brSnapCache && brSnapCache.clearedIds && typeof brSnapCache.clearedIds === 'object') {
        for (const k of Object.keys(brSnapCache.clearedIds)) {
            if (brSnapCache.clearedIds[k]) clearedSet.add(k);
        }
    }
    brCleared = clearedSet;
    body.innerHTML = rows
        .map((t) => {
            const ms = Number(t.clientLocalAt) || Number(t.createdAtMs) || 0;
            const when = ms
                ? new Date(ms).toLocaleString('bn-BD', { dateStyle: 'short', timeStyle: 'short' })
                : '—';
            const signed = signedAmountForAccount(t, accountId);
            const kind = String(t.kind || 'manual');
            const chk = brCleared.has(t.id) ? 'checked' : '';
            return `<tr data-br-id="${escHtml(t.id)}">
                <td class="px-2 py-1"><input type="checkbox" class="br-cb accent-sky-500" data-id="${escHtml(
                    t.id
                )}" ${chk}></td>
                <td class="px-2 py-1 text-zinc-400 text-xs">${escHtml(when)}</td>
                <td class="px-2 py-1 text-xs">${escHtml(kind)}</td>
                <td class="px-2 py-1 text-xs">${signed >= 0 ? '+' : ''}${escHtml(plainMoney(signed))}</td>
                <td class="px-2 py-1 text-right tabular-nums">${escHtml(money(t.amount))}</td>
            </tr>`;
        })
        .join('');
    body.querySelectorAll('.br-cb').forEach((cb) => {
        cb.addEventListener('change', () => brRecomputeLive());
    });
    brRecomputeLive();
}

function brRecomputeLive() {
    const accountId = document.getElementById('br-account')?.value || 'bank';
    let sumClr = 0;
    document.querySelectorAll('.br-cb').forEach((cb) => {
        if (!(cb instanceof HTMLInputElement) || !cb.checked) return;
        const id = cb.getAttribute('data-id');
        const t = brRows.find((x) => x.id === id);
        if (t) sumClr += signedAmountForAccount(t, accountId);
    });
    sumClr = roundMoney(sumClr);
    const bookStart = roundMoney(parseFloat(document.getElementById('br-book-start')?.value || '0') || 0);
    const expected = roundMoney(bookStart + sumClr);
    const st = roundMoney(parseFloat(document.getElementById('br-statement')?.value || '0') || 0);
    const exEl = document.getElementById('br-expected');
    if (exEl) exEl.value = plainMoney(expected);
    const varLive = roundMoney(st - expected);
    const vEl = document.getElementById('br-variance-live');
    if (vEl) vEl.textContent = money(varLive);
}

async function refreshReconVarianceBadge() {
    const pk = document.getElementById('br-period')?.value || '';
    const acc = document.getElementById('br-account')?.value || 'bank';
    const meta = document.getElementById('acc-recon-meta');
    const vBadge = document.getElementById('acc-recon-variance');
    if (!vBadge) return;
    if (!CURRENT_CLIENT_ID || !pk) {
        vBadge.textContent = '—';
        if (meta) meta.textContent = 'পিরিয়ড নির্বাচন করুন';
        return;
    }
    const key = `${acc}|${pk}`;
    if (brSnapKey !== key) {
        try {
            const sn = await get(
                ref(db, `clients/${CURRENT_CLIENT_ID}/bankReconciliation/${acc}/${pk}`)
            );
            brSnapCache = sn.exists() ? sn.val() : null;
            brSnapKey = key;
        } catch (e) {
            console.warn(e);
            brSnapCache = null;
        }
    }
    const v = brSnapCache && brSnapCache.variance != null ? roundMoney(Number(brSnapCache.variance)) : null;
    vBadge.textContent = v == null ? '—' : money(v);
    if (meta) {
        const fin = brSnapCache?.finalized ? ' · locked' : '';
        meta.textContent = `${acc} · ${pk}${fin}`;
    }
}

function wireBankRecon() {
    if (WIRED_BANK_RECON) return;
    if (!document.getElementById('br-load')) return;
    WIRED_BANK_RECON = true;
    const per = document.getElementById('br-period');
    if (per && !per.value) {
        const d = new Date();
        const pad = (n) => String(n).padStart(2, '0');
        per.value = `${d.getFullYear()}-${pad(d.getMonth() + 1)}`;
    }
    document.getElementById('br-book-start')?.addEventListener('input', () => brRecomputeLive());
    document.getElementById('br-statement')?.addEventListener('input', () => brRecomputeLive());
    document.getElementById('br-load')?.addEventListener('click', async () => {
        if (!readAccountingFeatures(siteSettingsCacheAcc).bankReconciliation) {
            showToast('Bank reconciliation বন্ধ আছে।', false);
            return;
        }
        const acc = document.getElementById('br-account')?.value || 'bank';
        const pk = document.getElementById('br-period')?.value || '';
        if (!pk) {
            showToast('পিরিয়ড নির্বাচন করুন।', false);
            return;
        }
        brSnapKey = '';
        try {
            const sn = await get(ref(db, `clients/${CURRENT_CLIENT_ID}/bankReconciliation/${acc}/${pk}`));
            brSnapCache = sn.exists() ? sn.val() : null;
            brSnapKey = `${acc}|${pk}`;
            if (brSnapCache) {
                const bs = document.getElementById('br-book-start');
                const st = document.getElementById('br-statement');
                const fin = document.getElementById('br-finalize');
                if (bs && brSnapCache.bookAtStart != null) bs.value = String(brSnapCache.bookAtStart);
                if (st && brSnapCache.statementEnding != null) st.value = String(brSnapCache.statementEnding);
                if (fin) fin.checked = !!brSnapCache.finalized;
            }
            renderBrTable(acc);
            const hint = document.getElementById('br-snap-hint');
            if (hint) hint.textContent = brSnapCache?.finalized ? 'পিরিয়ড লক করা — আনলক করতে Unlock চাপুন।' : '';
            void refreshReconVarianceBadge();
            showToast('লোড হয়েছে।', true);
        } catch (err) {
            logInitStage('bankReconLoad', err);
            showToast(formatFirebaseInitError(err), false);
        }
    });
    document.getElementById('br-save')?.addEventListener('click', async () => {
        if (!readAccountingFeatures(siteSettingsCacheAcc).bankReconciliation) {
            showToast('Bank reconciliation বন্ধ আছে।', false);
            return;
        }
        const acc = document.getElementById('br-account')?.value || 'bank';
        const pk = document.getElementById('br-period')?.value || '';
        if (!CURRENT_CLIENT_ID || !pk) return;
        if (brSnapCache?.finalized) {
            showToast('লক করা স্ন্যাপশট — আগে আনলক করুন।', false);
            return;
        }
        const clearedIds = {};
        document.querySelectorAll('.br-cb').forEach((cb) => {
            if (cb instanceof HTMLInputElement && cb.checked && cb.getAttribute('data-id')) {
                clearedIds[cb.getAttribute('data-id')] = true;
            }
        });
        let sumClr = 0;
        for (const id of Object.keys(clearedIds)) {
            const t = brRows.find((x) => x.id === id);
            if (t) sumClr += signedAmountForAccount(t, acc);
        }
        sumClr = roundMoney(sumClr);
        const bookStart = roundMoney(parseFloat(document.getElementById('br-book-start')?.value || '0') || 0);
        const statementEnding = roundMoney(parseFloat(document.getElementById('br-statement')?.value || '0') || 0);
        const expectedBookEnd = roundMoney(bookStart + sumClr);
        const variance = roundMoney(statementEnding - expectedBookEnd);
        const finalize = !!document.getElementById('br-finalize')?.checked;
        const ts = Date.now();
        const uid = auth.currentUser?.uid || null;
        const closeLock = readAccountingFeatures(siteSettingsCacheAcc).periodCloseLock;
        try {
            const patch = {
                [`clients/${CURRENT_CLIENT_ID}/bankReconciliation/${acc}/${pk}`]: {
                    accountId: acc,
                    periodKey: pk,
                    bookAtStart: bookStart,
                    statementEnding,
                    sumClearedSigned: sumClr,
                    expectedBookEnd,
                    variance,
                    clearedIds,
                    finalized: finalize,
                    updatedAtMs: ts,
                    updatedByUid: uid
                }
            };
            if (finalize && closeLock) {
                patch[`clients/${CURRENT_CLIENT_ID}/accountingSettings/closedMonths/${pk}`] = {
                    closed: true,
                    atMs: ts,
                    byUid: uid,
                    source: 'bank_recon_finalize',
                    accountId: acc
                };
            }
            await update(ref(db), patch);
            brSnapCache = {
                accountId: acc,
                periodKey: pk,
                bookAtStart: bookStart,
                statementEnding,
                sumClearedSigned: sumClr,
                expectedBookEnd,
                variance,
                clearedIds,
                finalized: finalize
            };
            brSnapKey = `${acc}|${pk}`;
            void refreshReconVarianceBadge();
            if (document.getElementById('acc-ledger-pos-sales')) refreshAccountingLedgerSummaries();
            showToast('স্ন্যাপশট সেভ হয়েছে।', true);
        } catch (err) {
            console.error(err);
            showToast(err?.message || 'সেভ ব্যর্থ।', false);
        }
    });
    document.getElementById('br-reopen')?.addEventListener('click', async () => {
        const acc = document.getElementById('br-account')?.value || 'bank';
        const pk = document.getElementById('br-period')?.value || '';
        if (!CURRENT_CLIENT_ID || !pk) return;
        const uid = auth.currentUser?.uid || null;
        const evId = push(ref(db, `clients/${CURRENT_CLIENT_ID}/accountingSettings/periodReopenEvents`)).key;
        try {
            const patch = {
                [`clients/${CURRENT_CLIENT_ID}/bankReconciliation/${acc}/${pk}/finalized`]: false
            };
            if (readAccountingFeatures(siteSettingsCacheAcc).periodCloseLock) {
                patch[`clients/${CURRENT_CLIENT_ID}/accountingSettings/closedMonths/${pk}`] = null;
            }
            if (evId) {
                patch[`clients/${CURRENT_CLIENT_ID}/accountingSettings/periodReopenEvents/${evId}`] = {
                    periodKey: pk,
                    accountId: acc,
                    atMs: Date.now(),
                    byUid: uid,
                    source: 'bank_recon_unlock'
                };
            }
            await update(ref(db), patch);
            if (brSnapCache) brSnapCache.finalized = false;
            const fin = document.getElementById('br-finalize');
            if (fin) fin.checked = false;
            showToast('আনলক করা হয়েছে।', true);
        } catch (err) {
            console.error(err);
            showToast('ব্যর্থ।', false);
        }
    });
}

function wireRangeButtons() {
    if (WIRED_RANGE_DELEGATE) return;
    WIRED_RANGE_DELEGATE = true;
    document.addEventListener('click', (e) => {
        const btn = e.target && e.target.closest && e.target.closest('.acc-range-btn');
        if (!btn || !(btn instanceof HTMLElement)) return;
        const r = btn.getAttribute('data-range');
        if (!r) return;
        rangePreset = r;
        document.querySelectorAll('.acc-range-btn').forEach((b) =>
            b.classList.toggle('is-active', b.getAttribute('data-range') === r)
        );
        refreshRangeCards();
    });
}

function wireHistoryFilters() {
    if (WIRED_HISTORY_FILTERS) return;
    if (!document.querySelector('#tf-history-filters [data-tf-filter]')) return;
    WIRED_HISTORY_FILTERS = true;
    document.querySelectorAll('#tf-history-filters [data-tf-filter]').forEach((btn) => {
        btn.addEventListener('click', () => {
            historyFilter = btn.getAttribute('data-tf-filter') || 'all';
            document.querySelectorAll('#tf-history-filters [data-tf-filter]').forEach((b) =>
                b.classList.toggle('is-active', b.getAttribute('data-tf-filter') === historyFilter)
            );
            renderHistory();
        });
    });
}

function setDefaultTransferAt() {
    const el = document.getElementById('tf-at');
    if (!el || el.value) return;
    const d = new Date();
    const pad = (n) => String(n).padStart(2, '0');
    el.value = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

function wireTransferForm() {
    if (WIRED_TRANSFER_FORM) return;
    if (!document.getElementById('tf-submit')) return;
    WIRED_TRANSFER_FORM = true;
    const fromSel = document.getElementById('tf-from');
    const toSel = document.getElementById('tf-to');
    fromSel?.addEventListener('change', syncTransferPreviewBalances);
    toSel?.addEventListener('change', syncTransferPreviewBalances);
    document.getElementById('tf-submit')?.addEventListener('click', async () => {
        if (transferSubmitBusy) return;
        const user = auth.currentUser;
        if (!user || !CURRENT_CLIENT_ID) return;
        const fromId = document.getElementById('tf-from')?.value;
        const toId = document.getElementById('tf-to')?.value;
        const amount = Math.round((parseFloat(document.getElementById('tf-amount')?.value || '0') || 0) * 100) / 100;
        const referenceId = String(document.getElementById('tf-ref')?.value || '').trim();
        const remark = String(document.getElementById('tf-remark')?.value || '').trim();
        const atStr = document.getElementById('tf-at')?.value;
        let clientLocalAt = Date.now();
        if (atStr) {
            const parsed = new Date(atStr);
            if (!Number.isNaN(parsed.getTime())) clientLocalAt = parsed.getTime();
        }
        if (!fromId || !toId) {
            showToast('From / To নির্বাচন করুন।', false);
            return;
        }
        if (fromId === toId) {
            showToast('From ও To একই হতে পারবে না।', false);
            return;
        }
        if (amount <= 0) {
            showToast('সঠিক amount দিন।', false);
            return;
        }
        const accSnap = await get(ref(db, `clients/${CURRENT_CLIENT_ID}/fundAccounts`));
        const accVal = accSnap.val() || {};
        const fromBal = Number(accVal[fromId]?.balance) || 0;
        if (fromBal < amount) {
            showToast('From অ্যাকাউন্টে পর্যাপ্ত ব্যালান্স নেই।', false);
            return;
        }
        if (!(await assertMonthOpenForPosting(clientLocalAt))) return;
        transferSubmitBusy = true;
        const tid = push(ref(db, `clients/${CURRENT_CLIENT_ID}/fundTransfers`)).key;
        const base = `clients/${CURRENT_CLIENT_ID}`;
        const createdAtMs = Date.now();
        try {
            await update(ref(db), {
                [`${base}/fundTransfers/${tid}`]: {
                    fromAccountId: fromId,
                    toAccountId: toId,
                    amount,
                    referenceId: referenceId || null,
                    remark: remark || null,
                    clientLocalAt,
                    createdAtMs,
                    createdAt: serverTimestamp(),
                    createdByUid: user.uid,
                    kind: 'manual',
                    status: 'posted'
                },
                [`${base}/fundAccounts/${fromId}/balance`]: increment(-amount),
                [`${base}/fundAccounts/${toId}/balance`]: increment(amount),
                [`${base}/fundAccounts/${fromId}/updatedAt`]: serverTimestamp(),
                [`${base}/fundAccounts/${toId}/updatedAt`]: serverTimestamp()
            });
            showToast('ট্রান্সফার সেভ হয়েছে।', true);
            document.getElementById('tf-amount').value = '';
            document.getElementById('tf-ref').value = '';
            document.getElementById('tf-remark').value = '';
            setDefaultTransferAt();
        } catch (err) {
            console.error(err);
            showToast(err?.message || 'সেভ ব্যর্থ।', false);
        } finally {
            transferSubmitBusy = false;
        }
    });
}

function mapHashToAccountingPage() {
    const h = (typeof location !== 'undefined' && location.hash ? location.hash.slice(1) : '').trim().toLowerCase();
    const allowed = new Set(['hub', 'transfer', 'opex', 'recon', 'settlement', 'ar']);
    if (allowed.has(h)) return /** @type {'hub'|'transfer'|'opex'|'recon'|'settlement'|'ar'} */ (h);
    return 'hub';
}

function applyAccRouteVisibility(page) {
    document.querySelectorAll('[data-acc-route]').forEach((el) => {
        const r = el.getAttribute('data-acc-route');
        const hide = r !== page;
        el.classList.toggle('hidden', hide);
        el.setAttribute('aria-hidden', hide ? 'true' : 'false');
    });
    document.querySelectorAll('a.sidebar-sublink[data-acc-hash]').forEach((a) => {
        const p = a.getAttribute('data-acc-hash') || '';
        const isActive = p === page;
        a.classList.toggle('active', isActive);
        if (isActive) a.setAttribute('aria-current', 'page');
        else a.removeAttribute('aria-current');
    });
}

export function switchAccountingPage(page) {
    const allowed = new Set(['hub', 'transfer', 'opex', 'recon', 'settlement', 'ar']);
    const p = allowed.has(page) ? page : 'hub';
    ACC_PAGE = p;
    if (typeof history !== 'undefined' && history.replaceState) {
        const base = location.pathname + location.search;
        history.replaceState({ accPage: p }, '', p === 'hub' ? base : `${base}#${p}`);
    }
    applyAccRouteVisibility(p);
    if (p === 'transfer') setDefaultTransferAt();
    if (CURRENT_CLIENT_ID) startLiveData(CURRENT_CLIENT_ID, p);
    setAccRangeLabelText();
}

function installAccountingHashRoutingOnce() {
    if (HASH_ROUTING_INSTALLED) return;
    HASH_ROUTING_INSTALLED = true;
    window.addEventListener('hashchange', () => {
        const next = mapHashToAccountingPage();
        if (next === ACC_PAGE) return;
        switchAccountingPage(next);
    });
}

export function startLiveData(clientId, page = ACC_PAGE) {
    if (typeof unsubReceivable === 'function') {
        unsubReceivable();
        unsubReceivable = null;
    }
    if (typeof unsubDashSummary === 'function') {
        unsubDashSummary();
        unsubDashSummary = null;
    }
    if (typeof unsubOrders === 'function') {
        unsubOrders();
        unsubOrders = null;
    }
    if (typeof unsubWallet === 'function') {
        unsubWallet();
        unsubWallet = null;
    }
    if (typeof unsubFundAccounts === 'function') {
        unsubFundAccounts();
        unsubFundAccounts = null;
    }
    if (typeof unsubTransfers === 'function') {
        unsubTransfers();
        unsubTransfers = null;
    }
    if (typeof unsubOpex === 'function') {
        unsubOpex();
        unsubOpex = null;
    }
    if (typeof unsubDistributors === 'function') {
        unsubDistributors();
        unsubDistributors = null;
    }

    const wantOrders = page === 'hub' || page === 'settlement';
    const wantDistributors = page === 'hub';
    const wantOpex = page === 'hub' || page === 'opex';
    const wantReceivable = page === 'ar';
    const wantTransfers = page === 'hub' || page === 'transfer' || page === 'recon' || page === 'settlement';
    const wantFundAccounts = page !== 'recon' && page !== 'opex';

    if (wantOrders) {
        unsubOrders = onValue(ref(db, `clients/${clientId}/orders`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/orders`, 'onValue', snap);
            const v = snap.val() || {};
            ordersCache = Object.values(v);
            refreshRangeCards();
        });
    }

    const needWalletSync =
        document.getElementById('sidebar-wallet-balance') || document.getElementById('acc-platform-wallet');
    if (needWalletSync) {
        unsubWallet = onValue(ref(db, `clients/${clientId}/wallet/currentBalance`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/wallet/currentBalance`, 'onValue', snap);
            platformWalletBalance = Number(snap.val()) || 0;
            const sw = document.getElementById('sidebar-wallet-balance');
            if (sw) sw.textContent = platformWalletBalance.toLocaleString('bn-BD', { maximumFractionDigits: 2 });
            refreshFundSummaryCards();
            syncTransferPreviewBalances();
        });
    }

    if (wantFundAccounts) {
        unsubFundAccounts = onValue(ref(db, `clients/${clientId}/fundAccounts`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/fundAccounts`, 'onValue', snap);
            const v = snap.val() || {};
            fundAccountsCache = { ...v };
            delete fundAccountsCache._meta;
            refreshFundSummaryCards();
            syncTransferPreviewBalances();
        });
    }

    if (wantTransfers) {
        const tRef = ref(db, `clients/${clientId}/fundTransfers`);
        const xferCb = (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/fundTransfers`, 'onValue', snap);
            const v = snap.val() || {};
            transfersList = Object.entries(v)
                .filter(([k]) => k !== '_meta')
                .map(([id, row]) => ({ id, ...row }))
                .sort((a, b) => (Number(b.createdAtMs) || 0) - (Number(a.createdAtMs) || 0))
                .slice(0, 500);
            renderHistory();
            refreshAccountingLedgerSummaries();
        };
        if (page === 'recon') {
            unsubTransfers = onValue(tRef, xferCb);
        } else {
            const qRef = query(tRef, orderByChild('createdAtMs'), limitToLast(FUND_TRANSFERS_QUERY_LIMIT));
            unsubTransfers = onValue(qRef, xferCb);
        }
    }

    if (wantReceivable) {
        unsubReceivable = onValue(ref(db, `clients/${clientId}/customersReceivable`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/customersReceivable`, 'onValue', snap);
            receivablesCache = snap.val() || {};
            renderArList();
        });
    }

    if (wantOpex) {
        unsubOpex = onValue(ref(db, `clients/${clientId}/operatingExpenses`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/operatingExpenses`, 'onValue', snap);
            operatingExpensesVal = snap.val() || {};
            renderOxList();
        });
    }

    if (wantDistributors) {
        unsubDistributors = onValue(ref(db, `clients/${clientId}/distributors`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/distributors`, 'onValue', snap);
            const due = sumSupplierDue(snap.val());
            const el = document.getElementById('acc-supplier-due');
            if (el) el.textContent = money(due);
        });
    }

    const dashHint = document.getElementById('acc-dash-summary-hint');
    if (dashHint && page === 'hub') {
        unsubDashSummary = onValue(ref(db, `clients/${clientId}/dashboardSummary/latest`), (snap) => {
            recordRtdbTelemetry(`clients/${clientId}/dashboardSummary/latest`, 'onValue', snap);
            const v = snap.val();
            if (!v || typeof v !== 'object') {
                dashHint.classList.add('hidden');
                return;
            }
            const age = Date.now() - (Number(v.updatedAtMs) || 0);
            if (age > 20 * 60 * 1000) {
                dashHint.classList.add('hidden');
                return;
            }
            const sales = Number(v.totalSales) || 0;
            const ord = Number(v.paidOrders) || 0;
            const vis = Number(v.totalVisitors) || 0;
            const lbl = v.currentPeriodRangeLabel ? String(v.currentPeriodRangeLabel) : '';
            dashHint.textContent =
                `ড্যাশবোর্ড সারাংশ (সম্প্রতি আপডেট${lbl ? ` · ${lbl}` : ''}): বিক্রয় ${money(sales)} · পেইড অর্ডার ${ord} · ভিজিটর ${vis}`;
            dashHint.classList.remove('hidden');
        });
    }
}

export function bootstrapAccounting(forcedPage) {
    const initial =
        forcedPage != null && forcedPage !== ''
            ? /** @type {'hub'|'transfer'|'opex'|'recon'|'settlement'|'ar'} */ (forcedPage)
            : mapHashToAccountingPage();
    ACC_PAGE = initial;

    wireRangeButtons();
    wireAccountingFeatureForm();
    wireTransferForm();
    wireHistoryFilters();
    wireOperatingExpenseForm();
    wireBankRecon();
    wireArPanel();
    setAccRangeLabelText();

    onAuthStateChanged(auth, async (user) => {
    const gate = document.getElementById('auth-gate');
    const root = document.getElementById('acc-root');
    if (!user) {
        window.location.href = 'setup.html';
        return;
    }
    let siteInfo = {};
    try {
        siteInfo = JSON.parse(localStorage.getItem('siteInfo') || '{}');
    } catch (e) {
        siteInfo = {};
    }
    if (!siteInfo.clientId) {
        window.location.href = 'setup.html';
        return;
    }

    try {
        const adminSnap = await get(ref(db, `clients/${siteInfo.clientId}/admins/${user.uid}`));
        if (!adminSnap.exists()) {
            showToast('অ্যাডমিন অ্যাক্সেস নেই।', false);
            window.location.href = 'index.html';
            return;
        }
        const adminVal = adminSnap.val();
        if (!staffHasStrictPanelPermission(adminVal, 'accounting')) {
            showToast('এই পেজে আপনার অনুমতি নেই।', false);
            window.location.href = 'index.html';
            return;
        }
    } catch (e) {
        logInitStage('adminRead', e);
        showToast(formatFirebaseInitError(e), false);
        return;
    }

    CURRENT_CLIENT_ID = siteInfo.clientId;

    try {
        await ensureFundAccounts(db, CURRENT_CLIENT_ID, { ref, get, update, serverTimestamp });
    } catch (e) {
        logInitStage('ensureFundAccounts', e);
        console.warn('[Accounting] ensureFundAccounts', e);
        showToast(
            'ফান্ড অ্যাকাউন্ট সিড সমস্যা—পেজ চালু আছে। নেটওয়ার্ক/অনুমতি চেক করুন।',
            false
        );
    }

    try {
        if (document.getElementById('af-pos')) {
            await loadAccountingSettingsUi();
        } else {
            await loadAccountingSettingsIntoCache();
            applyAccountingFeatureVisibility();
        }
    } catch (e) {
        logInitStage('loadAccountingSettingsUi', e);
        siteSettingsCacheAcc = {};
        applyAccountingFeatureVisibility();
        showToast('সাইট সেটিংস লোড হয়নি—ডিফল্ট ফ্ল্যাগ ব্যবহার হচ্ছে।', false);
    }

    try {
        applyAccRouteVisibility(ACC_PAGE);
        installAccountingHashRoutingOnce();
        if (ACC_PAGE === 'transfer') setDefaultTransferAt();
        startLiveData(CURRENT_CLIENT_ID, ACC_PAGE);
        gate?.classList.add('hidden');
        root?.classList.remove('hidden');
    } catch (e) {
        logInitStage('startLiveDataOrReveal', e);
        showToast(formatFirebaseInitError(e), false);
    }
});
}
