/**
 * String interpolation validator for Acurity Architect.
 * Validates variables used in output statements with ^variable^ syntax.
 */

import { isDatabaseHandle } from "../constants.js";
import { createDiagnostic } from "../diagnostics/diagnosticFactory.js";
import { getInterpolationTargetsForLine } from "../analysis/executionContext.js";

/**
 * Validates string interpolation in output statements.
 * @param {vscode.TextDocument} document - The document to validate
 * @param {Function} getRuleSeverity - Severity resolver
 * @param {Object} [options={}] - Optional precomputed execution context
 * @param {Map<string, Object>} [options.declaredVariables] - Map of declared variables
 * @param {Array} options.executionClassificationMap - Precomputed execution classification by line index.
 * **Required** — without it the validator cannot identify output lines and returns an empty array.
 * @param {Array<Array<{variableName:string, startPos:number, endPos:number, hasClosing:boolean, isFunctionLike:boolean}>>} [options.interpolationTargetsByLine]
 * Precomputed interpolation targets by line index
 * @returns {vscode.Diagnostic[]} - Array of interpolation diagnostics
 */
function validateStringInterpolation(document, getRuleSeverity, options = {}) {
    const diagnostics = [];
    const declaredVariables = options.declaredVariables || new Map();
    const executionClassificationMap = options.executionClassificationMap || null;

    // Without the classification map we cannot identify output lines — nothing to validate.
    if (!executionClassificationMap) {
        return diagnostics;
    }
    const outputDelimiters = options.outputDelimiters || { oicc: "^", cicc: "^" };
    const includedVariablesByLine = options.includedVariablesByLine || null;
    const declaredVariablesByLowerCase = new Map();
    const declaredVariableNames = new Set();

    declaredVariables.forEach((varInfo) => {
        const variableName = varInfo.name;
        declaredVariableNames.add(variableName);
        const lower = variableName.toLowerCase();
        if (!declaredVariablesByLowerCase.has(lower)) {
            declaredVariablesByLowerCase.set(lower, variableName);
        }
    });

    for (let lineIndex = 0; lineIndex < document.lineCount; lineIndex += 1) {
        const lineText = document.lineAt(lineIndex).text;

        // Interpolation checks only apply in output text lines.
        if (!executionClassificationMap[lineIndex].isOutput) {
            continue;
        }

        const interpolationTargets = options.interpolationTargetsByLine?.[lineIndex]
            || getInterpolationTargetsForLine(lineText, outputDelimiters);

        for (const target of interpolationTargets) {
            const { variableName, startPos, endPos, hasClosing, isFunctionLike } = target;

            // Skip if this looks like a function call rather than variable interpolation.
            if (isFunctionLike) {
                continue;  // This is a function call, not a variable interpolation
            }

            // Check if variable is declared OR if it's a database handle
            const isDbHandle = isDatabaseHandle(variableName);
            if (!declaredVariableNames.has(variableName) && !isDbHandle) {
                // Also check variables from included files.
                const includedVars = includedVariablesByLine?.get(lineIndex);
                if (includedVars && includedVars.has(variableName)) {
                    // Found in includes — proceed to unclosed-interpolation check below.
                } else {
                    const caseMatch = declaredVariablesByLowerCase.get(variableName.toLowerCase())
                        || (includedVars && (() => {
                            const lower = variableName.toLowerCase();
                            for (const name of includedVars.keys()) {
                                if (name.toLowerCase() === lower) return name;
                            }
                            return null;
                        })());
                    if (caseMatch) {
                        const caseMismatchDiag = createDiagnostic(
                            { start: { line: lineIndex, character: startPos }, end: { line: lineIndex, character: endPos } },
                            { messageId: "ACU-INT-002", params: { variableName, declaredName: caseMatch } },
                            getRuleSeverity
                        );
                        if (caseMismatchDiag) {
                            diagnostics.push(caseMismatchDiag);
                        }
                        continue;
                    }

                    const undefinedDiag = createDiagnostic(
                        { start: { line: lineIndex, character: startPos }, end: { line: lineIndex, character: endPos } },
                        { messageId: "ACU-INT-001", params: { variableName } },
                        getRuleSeverity
                    );
                    if (undefinedDiag) {
                        diagnostics.push(undefinedDiag);
                    }
                    continue;
                }
            }

            // Check for unclosed interpolation (only one ^)
            if (!hasClosing) {
                const unclosedDiag = createDiagnostic(
                    { start: { line: lineIndex, character: startPos }, end: { line: lineIndex, character: endPos } },
                    { messageId: "ACU-INT-003", params: { variableName } },
                    getRuleSeverity
                );
                if (unclosedDiag) {
                    diagnostics.push(unclosedDiag);
                }
            }
        }
    }

    return diagnostics;
}

export {
    validateStringInterpolation
};
