/**
 * OPTION directive validator for Acurity Architect.
 *
 * Validates OPTION directive settings:
 *   ACU-OPT-001  ICC/OICC/CICC character is not in the allowed set
 *   ACU-OPT-002  ICC character is valid but not the default '#' (IDE support limitation)
 *
 * Allowed characters for ICC, OICC, and CICC:
 *   ! @ # % ^ | ~ ?
 *
 * The default ICC character is '#'. Using any other valid character triggers
 * ACU-OPT-002 because IDE interpolation support requires the default value.
 */

import { createDiagnostic } from "../diagnostics/diagnosticFactory.js";

const ALLOWED_OPTION_CHARS = new Set(["!", "@", "#", "%", "^", "|", "~", "?"]);
const ALLOWED_CHARS_DISPLAY = "!, @, #, %, ^, |, ~, ?";
const DEFAULT_ICC_CHAR = "#";

/**
 * Extracts all KEY='value' pairs from an OPTION directive line.
 * Handles single and double quotes; returns one entry per match.
 *
 * @param {string} lineText - Full line text
 * @returns {{ key: string, value: string, charPos: number }[]}
 *   key      - uppercased key name (e.g. "OICC")
 *   value    - the single character between the quotes (may be empty string)
 *   charPos  - zero-based column of the value character in the original line
 */
function extractOptionPairs(lineText) {
    const pairs = [];
    // Matches: WORD = 'x'  or  WORD = "x"  (x may be absent for empty quotes)
    const regex = /(\w+)\s*=\s*(['"])(.?)\2/g;
    let match;
    while ((match = regex.exec(lineText)) !== null) {
        const key = match[1].toUpperCase();
        // Skip keys that are not OPTION setting names (e.g. avoid matching random identifiers)
        if (key !== "ICC" && key !== "OICC" && key !== "CICC") continue;

        const value = match[3];
        // Position of the value char: start of full match + offset to opening quote + 1
        const eqOffset = match[0].indexOf("=");
        const quoteOffset = match[0].indexOf(match[2], eqOffset + 1);
        const charPos = match.index + quoteOffset + 1;
        pairs.push({ key, value, charPos });
    }
    return pairs;
}

/**
 * Returns true if the line is an OPTION directive.
 * @param {string} lineText
 * @returns {boolean}
 */
function isOptionLine(lineText) {
    return /^\s*#?\s*OPTION\b/i.test(lineText);
}

/**
 * Validates OPTION directive lines.
 *
 * @param {object} document
 * @param {Function} getRuleSeverity
 * @param {object} [options]
 * @returns {object[]}
 */
function validateOptionDirectives(document, getRuleSeverity, options = {}) {
    const diagnostics = [];

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

        const pairs = extractOptionPairs(lineText);

        for (const { key, value, charPos } of pairs) {
            const range = {
                start: { line: lineIndex, character: charPos },
                end:   { line: lineIndex, character: charPos + 1 }
            };

            if (!ALLOWED_OPTION_CHARS.has(value)) {
                // Character is not in the allowed set — error regardless of key.
                const diag = createDiagnostic(
                    range,
                    { messageId: "ACU-OPT-001", params: { char: value, allowed: ALLOWED_CHARS_DISPLAY } },
                    getRuleSeverity
                );
                if (diag) diagnostics.push(diag);
            } else if (key === "ICC" && value !== DEFAULT_ICC_CHAR) {
                // Valid ICC character but not the default '#' — IDE support limitation.
                const diag = createDiagnostic(
                    range,
                    { messageId: "ACU-OPT-002", params: { char: value } },
                    getRuleSeverity
                );
                if (diag) diagnostics.push(diag);
            }
        }
    }

    return diagnostics;
}

export { validateOptionDirectives };
