// assets/js/smartflow-graph-core.js

// انواع نود و یال
export const NODE_TYPES = {
    VOUCHER: 'VOUCHER',
    ACCOUNT: 'ACCOUNT',
    LINE: 'LINE',
    DIMENSION: 'DIMENSION', // generic, with sub-type by dimType
};

export const EDGE_TYPES = {
    LINE_VOUCHER: 'LINE_VOUCHER',
    LINE_ACCOUNT: 'LINE_ACCOUNT',
    LINE_DIM: 'LINE_DIM',
    FLOW: 'FLOW',
};

/**
 * ساختار کلی ورودی RawVoucher (فقط برای مستندسازی)
 *
 * @typedef {Object} RawVoucher
 * @property {string|number} id
 * @property {string} [number]        - شماره سند
 * @property {string} [date]
 * @property {string} [currency]      - ارز پیش‌فرض سند
 * @property {Object[]} lines
 * @property {string|number} [lines[].line_id]
 * @property {number} [lines[].line_no]
 * @property {string} [lines[].account_code]
 * @property {string} [lines[].account_name]
 * @property {number} [lines[].debit]     - مبلغ بدهکار
 * @property {number} [lines[].credit]    - مبلغ بستانکار
 * @property {string} [lines[].currency]  - اگر نباشد از currency سند
 * @property {Object} [lines[].dimensions] - مثل { cost_center: 'CC01', project: 'PRJ1', ... }
 */

/**
 * @typedef {Object} SmartFlowNode
 * @property {string} id
 * @property {string} type        - یکی از NODE_TYPES
 * @property {Object} data        - اطلاعات مرتبط (account_code, amount,...)
 */

/**
 * @typedef {Object} SmartFlowEdge
 * @property {string} id
 * @property {string} type        - یکی از EDGE_TYPES
 * @property {string} from        - node id
 * @property {string} to          - node id
 * @property {Object} data
 */

/**
 * @typedef {Object} SmartFlowGraph
 * @property {SmartFlowNode[]} nodes
 * @property {SmartFlowEdge[]} edges
 * @property {Object} views      - تعریف Viewها (account_level, line_level, dimension_level)
 */

/**
 * options نمونه:
 *
 * {
 *   view: 'account_level',      // view پیش‌فرض
 *   dimensions: ['cost_center', 'project'], // کدام ابعاد فعال باشند
 *   greedyFlow: true,           // الگوریتم حریصانه برای FLOW
 * }
 */

/**
 * ورودی RawVoucher را به یک گراف استاندارد SmartFlow تبدیل می‌کند.
 *
 * @param {RawVoucher} rawVoucher
 * @param {Object} [options]
 * @returns {SmartFlowGraph}
 */
export function buildGraphFromRaw(rawVoucher, options = {}) {
    const opts = {
        view: options.view || 'account_level',
        dimensions: options.dimensions || [],
        greedyFlow: options.greedyFlow !== false, // پیش‌فرض true
    };

    validateRawVoucher(rawVoucher);

    const nodes = [];
    const edges = [];

    // ۱) ساخت نود سند
    const voucherNode = buildVoucherNode(rawVoucher);
    nodes.push(voucherNode);

    // ۲) ساخت نود خطوط + نود حساب‌ها + نود ابعاد و یال‌های مربوطه
    const {
        lineNodes,
        accountNodes,
        dimensionNodes,
        lineVoucherEdges,
        lineAccountEdges,
        lineDimensionEdges,
    } = buildNodesAndBasicEdges(rawVoucher, voucherNode, opts);

    nodes.push(...lineNodes, ...accountNodes, ...dimensionNodes);
    edges.push(...lineVoucherEdges, ...lineAccountEdges, ...lineDimensionEdges);

    // ۳) ساخت یال‌های FLOW بین حساب‌ها (الگوریتم حریصانه ساده)
    if (opts.greedyFlow) {
        const flowEdges = buildFlowEdgesFromLines(lineNodes, accountNodes);
        edges.push(...flowEdges);
    }

    // ۴) ساخت Viewها
    const views = buildViews(nodes, edges, opts);

    return {
        nodes,
        edges,
        views,
    };
}

/**
 * بررسی مقدماتی اینکه rawVoucher حداقل اطلاعات لازم را دارد
 */
function validateRawVoucher(rawVoucher) {
    if (!rawVoucher) {
        throw new Error('RawVoucher is required');
    }
    if (!Array.isArray(rawVoucher.lines)) {
        throw new Error('RawVoucher.lines must be an array');
    }
}

/**
 * ساخت نود سند
 */
function buildVoucherNode(rawVoucher) {
    const id = `voucher:${rawVoucher.id ?? rawVoucher.number ?? 'unknown'}`;
    return {
        id,
        type: NODE_TYPES.VOUCHER,
        data: {
            voucher_id: rawVoucher.id,
            number: rawVoucher.number,
            date: rawVoucher.date,
            currency: rawVoucher.currency,
        },
    };
}

/**
 * ساخت نودهای LINE، ACCOUNT، DIMENSION و یال‌های پایه:
 *  - LINE_VOUCHER
 *  - LINE_ACCOUNT
 *  - LINE_DIM
 */
function buildNodesAndBasicEdges(rawVoucher, voucherNode, options) {
    const lineNodes = [];
    const accountNodesMap = new Map();     // key: account_code
    const dimensionNodesMap = new Map();   // key: dimType:dimValue

    const lineVoucherEdges = [];
    const lineAccountEdges = [];
    const lineDimensionEdges = [];

    rawVoucher.lines.forEach((line, index) => {
        const lineId = normalizeLineId(line, index);
        const currency = normalizeCurrency(line, rawVoucher);

        // نود Line
        const lineNode = {
            id: `line:${lineId}`,
            type: NODE_TYPES.LINE,
            data: {
                line_id: lineId,
                line_no: line.line_no ?? index + 1,
                account_code: line.account_code,
                account_name: line.account_name,
                debit: line.debit ?? 0,
                credit: line.credit ?? 0,
                currency,
                raw: line,
            },
        };
        lineNodes.push(lineNode);

        // یال LINE_VOUCHER
        lineVoucherEdges.push({
            id: `edge:line_voucher:${lineId}`,
            type: EDGE_TYPES.LINE_VOUCHER,
            from: lineNode.id,
            to: voucherNode.id,
            data: {},
        });

        // نود Account (اگر account_code وجود داشته باشد)
        if (line.account_code) {
            const accountKey = line.account_code;
            let accountNode = accountNodesMap.get(accountKey);

            if (!accountNode) {
                accountNode = {
                    id: `account:${accountKey}`,
                    type: NODE_TYPES.ACCOUNT,
                    data: {
                        account_code: line.account_code,
                        account_name: line.account_name,
                    },
                };
                accountNodesMap.set(accountKey, accountNode);
            }

            // یال LINE_ACCOUNT
            lineAccountEdges.push({
                id: `edge:line_account:${lineId}->${accountNode.id}`,
                type: EDGE_TYPES.LINE_ACCOUNT,
                from: lineNode.id,
                to: accountNode.id,
                data: {},
            });
        }

        // نودهای ابعاد (cost_center, project, ...) فقط اگر در options.dimensions آمده باشند
        if (line.dimensions && typeof line.dimensions === 'object') {
            options.dimensions.forEach((dimType) => {
                const value = line.dimensions[dimType];
                if (!value) return;

                const dimKey = `${dimType}:${value}`;
                let dimNode = dimensionNodesMap.get(dimKey);
                if (!dimNode) {
                    dimNode = {
                        id: `dim:${dimKey}`,
                        type: NODE_TYPES.DIMENSION,
                        data: {
                            dim_type: dimType,
                            value,
                        },
                    };
                    dimensionNodesMap.set(dimKey, dimNode);
                }

                // یال LINE_DIM
                lineDimensionEdges.push({
                    id: `edge:line_dim:${lineId}->${dimNode.id}`,
                    type: EDGE_TYPES.LINE_DIM,
                    from: lineNode.id,
                    to: dimNode.id,
                    data: {},
                });
            });
        }
    });

    return {
        lineNodes,
        accountNodes: Array.from(accountNodesMap.values()),
        dimensionNodes: Array.from(dimensionNodesMap.values()),
        lineVoucherEdges,
        lineAccountEdges,
        lineDimensionEdges,
    };
}

/**
 * الگوریتم حریصانه ساده برای ساخت یال‌های FLOW بین حساب‌ها
 * - فرض ساده: از خطوط بدهکار به خطوط بستانکار بر اساس مبلغ، به‌صورت greedy
 * - بعداً می‌توانیم آن را هوشمندتر کنیم.
 */
function buildFlowEdgesFromLines(lineNodes, accountNodes) {
    const edges = [];

    // جداسازی debit و credit
    const debits = [];
    const credits = [];

    lineNodes.forEach((lineNode) => {
        const { debit = 0, credit = 0 } = lineNode.data;
        if (debit > 0) {
            debits.push({ lineNode, remaining: debit });
        }
        if (credit > 0) {
            credits.push({ lineNode, remaining: credit });
        }
    });

    let edgeCounter = 0;

    // الگوریتم حریصانه ساده: debit ها را با credit ها match می‌کنیم
    for (let d of debits) {
        for (let c of credits) {
            if (d.remaining <= 0) break;
            if (c.remaining <= 0) continue;

            const amount = Math.min(d.remaining, c.remaining);
            d.remaining -= amount;
            c.remaining -= amount;

            // در اینجا، ما یال FLOW را بین account های متناظر دو خط می‌سازیم
            const fromAccountId = d.lineNode.data.account_code
                ? `account:${d.lineNode.data.account_code}`
                : d.lineNode.id; // fallback: خود line
            const toAccountId = c.lineNode.data.account_code
                ? `account:${c.lineNode.data.account_code}`
                : c.lineNode.id;

            edges.push({
                id: `edge:flow:${edgeCounter++}`,
                type: EDGE_TYPES.FLOW,
                from: fromAccountId,
                to: toAccountId,
                data: {
                    amount,
                },
            });
        }
    }

    return edges;
}

/**
 * ساخت Viewهای مختلف (خیلی ساده – بعداً می‌توان دقیق‌ترش کرد)
 *
 * ایده: هر View می‌تواند زیرمجموعه‌ای از node/edge ها را نشان بدهد.
 */
function buildViews(nodes, edges, options) {
    const allNodeIds = nodes.map((n) => n.id);
    const allEdgeIds = edges.map((e) => e.id);

    const accountLevel = {
        nodes: allNodeIds,
        edges: allEdgeIds,
    };

    const lineLevel = {
        nodes: nodes.filter((n) => n.type === NODE_TYPES.LINE).map((n) => n.id),
        edges: edges
            .filter(
                (e) =>
                    e.type === EDGE_TYPES.LINE_VOUCHER ||
                    e.type === EDGE_TYPES.LINE_ACCOUNT ||
                    e.type === EDGE_TYPES.LINE_DIM
            )
            .map((e) => e.id),
    };

    const dimensionLevel = {
        nodes: nodes
            .filter(
                (n) =>
                    n.type === NODE_TYPES.DIMENSION ||
                    n.type === NODE_TYPES.LINE ||
                    n.type === NODE_TYPES.ACCOUNT
            )
            .map((n) => n.id),
        edges: edges
            .filter(
                (e) =>
                    e.type === EDGE_TYPES.LINE_DIM ||
                    e.type === EDGE_TYPES.LINE_ACCOUNT
            )
            .map((e) => e.id),
    };

    return {
        account_level: accountLevel,
        line_level: lineLevel,
        dimension_level: dimensionLevel,
        default: options.view || 'account_level',
    };
}

/**
 * اگر line_id نباشد، از line_no یا index استفاده می‌کنیم
 */
function normalizeLineId(line, index) {
    return (
        line.line_id ??
        line.line_no ??
        index + 1 // index 0-based → 1-based
    ).toString();
}

/**
 * اگر currency خط نباشد، از currency سند استفاده می‌کنیم
 */
function normalizeCurrency(line, voucher) {
    return line.currency || voucher.currency || 'IRR';
}
