"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fineractOfficeReachable = fineractOfficeReachable;
exports.postJournalEntry = postJournalEntry;
exports.resolveGlAccountId = resolveGlAccountId;
exports.fetchGlBalances = fetchGlBalances;
const axios_1 = __importDefault(require("axios"));
const GL_HINTS = ['1000', '1050', '1410', '2000', '2100', '2200', '2300'];
function fineractBase() {
    return (process.env.OMNL_FINERACT_BASE_URL || '').replace(/\/$/, '');
}
function authHeaders() {
    const tenant = process.env.OMNL_FINERACT_TENANT || 'omnl';
    const user = process.env.OMNL_FINERACT_USER?.trim() ||
        process.env.OMNL_FINERACT_USERNAME?.trim() ||
        'ali_hospitallers_tenant';
    const pass = process.env.OMNL_FINERACT_PASSWORD || '';
    if (!fineractBase() || !pass)
        throw new Error('Fineract credentials required');
    const auth = Buffer.from(`${user}:${pass}`).toString('base64');
    return {
        Authorization: `Basic ${auth}`,
        'Fineract-Platform-TenantId': tenant,
        'Content-Type': 'application/json',
        Accept: 'application/json',
    };
}
async function fineractOfficeReachable(officeId) {
    try {
        const { status } = await axios_1.default.get(`${fineractBase()}/offices/${officeId}`, {
            headers: authHeaders(),
            timeout: 20000,
        });
        return status === 200;
    }
    catch {
        return false;
    }
}
async function fetchOfficeJournalLines(officeId) {
    const headers = authHeaders();
    const out = [];
    let offset = 0;
    const limit = 200;
    for (;;) {
        const { data } = await axios_1.default.get(`${fineractBase()}/journalentries`, {
            headers,
            timeout: 60000,
            params: { officeId, limit, offset },
        });
        const batch = Array.isArray(data) ? data : data?.pageItems ?? [];
        if (!Array.isArray(batch) || batch.length === 0)
            break;
        for (const je of batch) {
            const row = je;
            const flat = typeof row.entryType === 'string'
                ? row.entryType
                : row.entryType?.value || row.entryType?.code || '';
            if (row.glAccountId != null && row.amount != null && flat) {
                const normalized = flat.toUpperCase();
                out.push({
                    glAccountId: row.glAccountId,
                    amount: Number(row.amount || 0),
                    entryType: normalized.startsWith('DEB') ? 'DEBIT' : 'CREDIT',
                });
                continue;
            }
            for (const d of row.debits ?? []) {
                if (d.glAccountId != null) {
                    out.push({ glAccountId: d.glAccountId, amount: Number(d.amount || 0), entryType: 'DEBIT' });
                }
            }
            for (const c of row.credits ?? []) {
                if (c.glAccountId != null) {
                    out.push({ glAccountId: c.glAccountId, amount: Number(c.amount || 0), entryType: 'CREDIT' });
                }
            }
        }
        if (batch.length < limit)
            break;
        offset += limit;
    }
    return out;
}
async function fetchGlAccountMap(ids) {
    const headers = authHeaders();
    const map = new Map();
    for (const code of GL_HINTS) {
        const { data } = await axios_1.default.get(`${fineractBase()}/glaccounts`, {
            headers,
            timeout: 30000,
            params: { glCode: code },
        });
        const rows = Array.isArray(data) ? data : data?.pageItems ?? [];
        for (const row of rows) {
            if (row.id != null && row.glCode)
                map.set(row.id, String(row.glCode));
        }
    }
    if ([...ids].some((id) => !map.has(id))) {
        let offset = 0;
        const limit = 200;
        for (;;) {
            const { data } = await axios_1.default.get(`${fineractBase()}/glaccounts`, {
                headers,
                timeout: 30000,
                params: { limit, offset },
            });
            const rows = Array.isArray(data) ? data : data?.pageItems ?? [];
            if (!rows.length)
                break;
            for (const row of rows) {
                if (row.id != null && row.glCode && ids.has(row.id))
                    map.set(row.id, String(row.glCode));
            }
            if (rows.length < limit)
                break;
            offset += limit;
        }
    }
    return map;
}
async function fetchGlBalancesFromJournals(officeId) {
    const lines = await fetchOfficeJournalLines(officeId);
    if (!lines.length)
        return {};
    const ids = new Set(lines.map((l) => l.glAccountId));
    const glById = await fetchGlAccountMap(ids);
    const net = new Map();
    for (const line of lines) {
        net.set(line.glAccountId, (net.get(line.glAccountId) ?? 0) + (line.entryType === 'DEBIT' ? line.amount : -line.amount));
    }
    const out = {};
    for (const [id, val] of net.entries()) {
        if (Math.abs(val) < 0.005)
            continue;
        const code = glById.get(id) ?? String(id);
        out[code] = Math.abs(val).toFixed(2);
    }
    return out;
}
async function postJournalEntry(entry) {
    const { data } = await axios_1.default.post(`${fineractBase()}/journalentries`, entry, {
        headers: authHeaders(),
        timeout: 60000,
    });
    return { resourceId: data.resourceId ?? data.entityId ?? 0 };
}
const glCodeCache = new Map();
/** Resolve Fineract GL account id from chart code (e.g. 1410 → 24). */
async function resolveGlAccountId(glCode) {
    const code = String(glCode).trim();
    const cached = glCodeCache.get(code);
    if (cached != null)
        return cached;
    const { data } = await axios_1.default.get(`${fineractBase()}/glaccounts`, {
        headers: authHeaders(),
        timeout: 30000,
        params: { glCode: code },
    });
    const rows = Array.isArray(data) ? data : data?.pageItems ?? [];
    const match = rows.find((r) => r.glCode === code);
    if (!match?.id) {
        throw new Error(`GL account not found for code ${code}`);
    }
    glCodeCache.set(code, match.id);
    return match.id;
}
async function fetchGlBalances(officeId) {
    const base = fineractBase();
    if (!base || !process.env.OMNL_FINERACT_PASSWORD)
        return {};
    try {
        const { data } = await axios_1.default.get(`${base}/runreports/General Ledger Report`, {
            headers: authHeaders(),
            params: { R_officeId: officeId, genericResultSet: false },
            timeout: 60000,
        });
        const out = {};
        const rows = Array.isArray(data) ? data : data?.data ?? [];
        for (const row of rows) {
            if (row.glCode)
                out[row.glCode] = String(row.balance ?? 0);
        }
        if (Object.keys(out).length > 0)
            return out;
    }
    catch {
        /* report unavailable on this Fineract — fall through */
    }
    try {
        return await fetchGlBalancesFromJournals(officeId);
    }
    catch {
        return {};
    }
}
