/**
 * Pure helpers: reorder suggestions & dead-stock candidates (client-side).
 */

/**
 * @param {Record<string, any>} products
 */
export function computeReorderSuggestions(products) {
    const out = [];
    if (!products || typeof products !== 'object') return out;
    for (const [id, p] of Object.entries(products)) {
        if (!p || !p.name) continue;
        if (!p.reorderEnabled) continue;
        const min = Number(p.reorderMin);
        const max = Number(p.reorderMax);
        if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) continue;
        if (p.hasVariants && p.variants) {
            for (const [vid, v] of Object.entries(p.variants)) {
                if (!v) continue;
                const vs = readManagedStockNumber(p, vid);
                if (vs === null) continue;
                if (vs >= min) continue;
                const sq = p.isWeighted
                    ? Math.max(0, Math.ceil((max - vs) * 1000) / 1000)
                    : Math.max(0, Math.round(max - vs));
                if (sq <= 0) continue;
                out.push({
                    productId: id,
                    variantId: vid,
                    name: `${String(p.name || id)} — ${String(v.name || vid)}`,
                    currentStock: vs,
                    reorderMin: min,
                    reorderMax: max,
                    suggestedQty: sq,
                    preferredSupplierId: p.preferredSupplierId || p.preferredSupplier || null,
                    leadTimeDays: p.leadTimeDays != null ? Number(p.leadTimeDays) : null
                });
            }
        } else {
            const stock = readManagedStockNumber(p, null);
            if (stock === null) continue;
            if (stock >= min) continue;
            const suggestedQty = p.isWeighted
                ? Math.max(0, Math.ceil((max - stock) * 1000) / 1000)
                : Math.max(0, Math.round(max - stock));
            if (suggestedQty <= 0) continue;
            out.push({
                productId: id,
                variantId: null,
                name: String(p.name || id),
                currentStock: stock,
                reorderMin: min,
                reorderMax: max,
                suggestedQty,
                preferredSupplierId: p.preferredSupplierId || p.preferredSupplier || null,
                leadTimeDays: p.leadTimeDays != null ? Number(p.leadTimeDays) : null
            });
        }
    }
    return out.sort((a, b) => a.currentStock - b.currentStock);
}

/**
 * @param {any} p
 * @param {string|null} variantId
 * @returns {number|null} null = unlimited / not managed
 */
export function readManagedStockNumber(p, variantId) {
    if (!p) return null;
    if (variantId && p.variants?.[variantId]) {
        const s = p.variants[variantId].stock;
        if (s === null || s === undefined || s === '') return null;
        const n = p.isWeighted ? parseFloat(s) : parseInt(s, 10);
        return Number.isNaN(n) ? 0 : n;
    }
    const s = p.stock;
    if (s === null || s === undefined || s === '') return null;
    const n = p.isWeighted ? parseFloat(s) : parseInt(s, 10);
    return Number.isNaN(n) ? 0 : n;
}

/**
 * @param {Record<string, any>} orders
 * @param {number} sinceMs
 * @returns {Map<string, number>} key `pid` or `pid::vid`
 */
export function aggregateSoldQtySince(orders, sinceMs) {
    const sold = new Map();
    if (!orders) return sold;
    for (const ord of Object.values(orders)) {
        if (!ord || !Array.isArray(ord.items)) continue;
        const t = orderTimeMs(ord);
        if (t < sinceMs) continue;
        const st = String(ord.status || '').toLowerCase();
        if (st === 'cancelled' || st === 'canceled') continue;
        for (const it of ord.items) {
            if (!it) continue;
            const pid = it.id || it.productId;
            if (!pid) continue;
            const q = Number(it.quantity) || 0;
            if (q <= 0) continue;
            const vid = it.variantId || null;
            const k = vid ? `${pid}::${vid}` : String(pid);
            sold.set(k, (sold.get(k) || 0) + q);
        }
    }
    return sold;
}

function orderTimeMs(ord) {
    const c = ord.createdAt;
    if (c && typeof c === 'object' && typeof c.toMillis === 'function') return c.toMillis();
    if (typeof c === 'number') return c;
    const placed = ord.placedAt || ord.timestamp || ord.date;
    if (typeof placed === 'number') return placed;
    if (typeof placed === 'string') {
        const d = Date.parse(placed);
        if (!Number.isNaN(d)) return d;
    }
    return 0;
}

/**
 * @param {Record<string, any>} products
 * @param {Record<string, any>} orders
 * @param {{ deadDays?: number, nowMs?: number }} opts
 */
export function computeDeadStockCandidates(products, orders, opts = {}) {
    const deadDays = opts.deadDays != null ? Number(opts.deadDays) : 60;
    const nowMs = opts.nowMs != null ? Number(opts.nowMs) : Date.now();
    const sinceMs = nowMs - deadDays * 86400000;
    const sold = aggregateSoldQtySince(orders, sinceMs);
    const out = [];
    if (!products) return out;
    for (const [id, p] of Object.entries(products)) {
        if (!p || !p.name) continue;
        const addRow = (variantId, label, stock) => {
            if (stock === null || stock <= 0) return;
            const k = variantId ? `${id}::${variantId}` : String(id);
            const moved = sold.get(k) || 0;
            if (moved > 0) return;
            out.push({
                productId: id,
                variantId,
                name: label,
                stock,
                deadDays
            });
        };
        if (p.hasVariants && p.variants) {
            for (const [vid, v] of Object.entries(p.variants)) {
                const st = readManagedStockNumber(p, vid);
                addRow(vid, `${String(p.name)} — ${String(v?.name || vid)}`, st);
            }
        } else {
            const st = readManagedStockNumber(p, null);
            addRow(null, String(p.name), st);
        }
    }
    return out.sort((a, b) => b.stock - a.stock);
}
