/**
 * Shared helpers used by multiple validators.
 * Kept here to avoid duplicating logic across validator files.
 */

/**
 * Builds a Map<UPPERCASE_NAME, type> for the variables visible in a given scope.
 * Includes both scope-local entries (keyed "<scopeId>:<name>") and global entries
 * (keyed "global:<name>"). Locals always win over globals when a name appears in both.
 *
 * @param {Map<string, {type: string}>} variableTypes - Keys are "scopeId:name" or "global:name"
 * @param {string} scopeId - "global" or the string form of the function start line
 * @returns {Map<string, string>} Uppercase name → type string
 */
function buildVariablesMap(variableTypes, scopeId) {
    const map = new Map();
    for (const [key, varInfo] of variableTypes) {
        const colonIdx = key.indexOf(":");
        const prefix = key.slice(0, colonIdx);
        const isGlobal = prefix === "global";
        const isLocal  = prefix === scopeId;
        if (!isGlobal && !isLocal) continue;
        const name = key.slice(colonIdx + 1).toUpperCase();
        // Locals always win; globals only write if the name is not yet present.
        if (isLocal) {
            map.set(name, varInfo.type);
        } else if (!map.has(name)) {
            map.set(name, varInfo.type);
        }
    }
    return map;
}

/**
 * Extracts the argument text from between the outer parentheses of a function-like call.
 * Handles nested parentheses and quoted strings correctly.
 *
 * @param {string} lineText - The full line (or code snippet) being scanned
 * @param {number} startPos - Position of the opening parenthesis
 * @returns {string|null} - The text between the outer parens, or null if the call is unclosed
 */
function extractFunctionArgs(lineText, startPos) {
    let depth = 0;
    let inString = false;
    let stringChar = null;

    for (let i = startPos; i < lineText.length; i++) {
        const char = lineText[i];

        if (!inString) {
            if (char === "'" || char === '"') {
                inString = true;
                stringChar = char;
            } else if (char === "(") {
                depth++;
            } else if (char === ")") {
                depth--;
                if (depth === 0) {
                    return lineText.substring(startPos + 1, i);
                }
            }
        } else {
            if (char === stringChar) {
                inString = false;
            }
        }
    }

    return null; // Unclosed
}

/**
 * Counts the number of comma-separated arguments in an argument string.
 * Handles nested parentheses and quoted strings correctly.
 *
 * @param {string} argsText - The text between the outer parentheses
 * @returns {number} - Number of arguments (0 for empty/blank string)
 */
function countArguments(argsText) {
    if (!argsText || argsText.trim() === "") {
        return 0;
    }

    let count = 1;
    let depth = 0;
    let inString = false;
    let stringChar = null;

    for (let i = 0; i < argsText.length; i++) {
        const char = argsText[i];

        if (!inString) {
            if (char === "'" || char === '"') {
                inString = true;
                stringChar = char;
            } else if (char === "(") {
                depth++;
            } else if (char === ")") {
                depth--;
            } else if (char === "," && depth === 0) {
                count++;
            }
        } else {
            if (char === stringChar) {
                inString = false;
            }
        }
    }

    return count;
}

export { buildVariablesMap, extractFunctionArgs, countArguments };
