/**
 * Shared macro analysis helpers.
 *
 * Centralizes macro declaration parsing and macro catalog facts so
 * validators and diagnostics consume one canonical model.
 */

import { parseMacroDeclaration, extractArgAt } from "../parser.js";

/**
 * Builds a first-declaration catalog for macros.
 * @param {import("vscode").TextDocument} document - Acurity document
 * @returns {Record<string, {lineIndex:number, declaredName:string, params:string[], body:string}>}
 */
function buildMacroCatalog(document) {
    const catalog = {};

    for (let lineIndex = 0; lineIndex < document.lineCount; lineIndex += 1) {
        const lineText = document.lineAt(lineIndex).text;
        const declaration = parseMacroDeclaration(lineText);
        if (!declaration) {
            continue;
        }

        const upperName = declaration.name.toUpperCase();

        // Only record first declaration
        if (catalog[upperName] !== undefined) {
            continue;
        }

        catalog[upperName] = {
            lineIndex,
            declaredName: declaration.name,
            params: declaration.params,
            body: declaration.body
        };
    }

    return catalog;
}

/**
 * Extracts macro invocations from a line of code.
 * Looks for identifiers followed by optional parentheses that match known macros.
 * @param {string} lineText - The line to analyze
 * @param {Record<string, {declaredName:string, params:string[]}>} macroCatalog - Known macros
 * @returns {Array<{name:string, upperName:string, startPos:number, endPos:number, hasParens:boolean, argCount:number|null}>}
 */
function extractMacroInvocations(lineText, macroCatalog) {
    const invocations = [];
    
    // Match identifiers, optionally followed by parentheses with arguments
    // Pattern: _identifier or _identifier(args)
    const pattern = /\b([A-Za-z_][A-Za-z0-9_]*)\s*(\([^)]*\))?/g;
    let match;

    while ((match = pattern.exec(lineText)) !== null) {
        const name = match[1];
        const upperName = name.toUpperCase();
        const parensCapture = match[2];
        const hasParens = !!parensCapture;
        const startPos = match.index;
        const endPos = match.index + match[0].length;

        // Check if this identifier is a known macro
        if (!macroCatalog[upperName]) {
            continue;
        }

        let argCount = null;
        if (hasParens) {
            // Count arguments using extractArgAt to correctly handle nested calls
            const callExpr = name + parensCapture;
            argCount = 0;
            while (extractArgAt(callExpr, argCount) !== null) {
                argCount++;
            }
        }

        invocations.push({
            name,
            upperName,
            startPos,
            endPos,
            hasParens,
            argCount
        });
    }

    return invocations;
}

export {
    buildMacroCatalog,
    extractMacroInvocations
};
