/**
 * Output-positioning directive validator for Acurity Architect.
 *
 * Output-positioning directives (CENTER, COL, FROM, TAB, TO) use function-call
 * syntax but are not functions — they are formatting instructions only valid
 * between OICC/CICC delimiters on output lines.
 *
 * ACU-OUT-001  outputDirectiveInCodeContext
 *   Fires when a directive is called outside an output-line context.
 *
 * ACU-OUT-002  outputDirectiveArgumentCount
 *   Fires when the wrong number of arguments is supplied.
 *
 * ACU-OUT-003  outputDirectiveColumnRange
 *   Fires when the column argument is outside the valid range (1–16000).
 */

import { createDiagnostic } from "../diagnostics/diagnosticFactory.js";
import { extractFunctionArgs, countArguments } from "./validatorHelpers.js";

const COLUMN_MIN = 1;
const COLUMN_MAX = 16000;

function addDiagnostic(diagnostics, range, messageId, params, getRuleSeverity) {
    const diag = createDiagnostic(range, { messageId, params }, getRuleSeverity);
    if (diag) diagnostics.push(diag);
}

/**
 * Returns the first comma-separated argument from an args string,
 * respecting nested parentheses and quoted strings.
 */
function getFirstArg(argsText) {
    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) { return argsText.substring(0, i); }
        } else {
            if (char === stringChar) { inString = false; }
        }
    }
    return argsText;
}

/**
 * Validates argument count and column range for an output-positioning directive call.
 * Only called when the directive is in a valid output-line context.
 */
function validateOutputDirectiveShape(
    code,
    match,
    lineIndex,
    actualCharPos,
    functionName,
    signature,
    diagnostics,
    getRuleSeverity
) {
    const startParen = match.index + match[0].length - 1;
    const argsText = extractFunctionArgs(code, startParen);

    if (argsText === null) {
        // Unclosed parentheses — the directive call is malformed; skip further checks.
        return;
    }

    const argCount = countArguments(argsText);
    const expectedCount = signature.params.length;

    if (argCount !== expectedCount) {
        addDiagnostic(
            diagnostics,
            { start: { line: lineIndex, character: actualCharPos }, end: { line: lineIndex, character: actualCharPos + match[0].length } },
            "ACU-OUT-002",
            { name: functionName.toUpperCase(), expected: expectedCount, actual: argCount },
            getRuleSeverity
        );
        return;
    }

    // Column/count range check: if the first argument is a plain integer literal,
    // verify it is within 1..16000 (the PAGEWIDTH default maximum).
    const firstArg = getFirstArg(argsText).trim();
    if (/^\d+$/.test(firstArg)) {
        const colValue = parseInt(firstArg, 10);
        if (colValue < COLUMN_MIN || colValue > COLUMN_MAX) {
            addDiagnostic(
                diagnostics,
                { start: { line: lineIndex, character: actualCharPos }, end: { line: lineIndex, character: actualCharPos + match[0].length } },
                "ACU-OUT-003",
                { value: colValue },
                getRuleSeverity
            );
        }
    }
}

/**
 * Validates an output-positioning directive call site.
 *
 * Call this from the function-call scanner whenever a token resolves to
 * kind === "output-directive". Handles both context-mismatch (ACU-OUT-001)
 * and shape validation (ACU-OUT-002/003) in one place.
 *
 * @param {string}   code             - Code snippet being scanned (quote-masked)
 * @param {object}   match            - Regex match object from the function-pattern scan
 * @param {number}   lineIndex        - Zero-based line number
 * @param {number}   actualCharPos    - Character offset of the directive name in the original line
 * @param {string}   functionName     - The directive name as written in source
 * @param {object}   knownFunction    - Registry entry (kind, signature)
 * @param {boolean}  isFromOutputLine - Whether the call site is inside OICC/CICC delimiters
 * @param {object[]} diagnostics      - Array to push diagnostics into
 * @param {Function} getRuleSeverity  - Severity resolver
 */
function validateOutputDirective(
    code,
    match,
    lineIndex,
    actualCharPos,
    functionName,
    knownFunction,
    isFromOutputLine,
    diagnostics,
    getRuleSeverity
) {
    if (!isFromOutputLine) {
        addDiagnostic(
            diagnostics,
            { start: { line: lineIndex, character: actualCharPos }, end: { line: lineIndex, character: actualCharPos + functionName.length } },
            "ACU-OUT-001",
            { name: functionName.toUpperCase() },
            getRuleSeverity
        );
        return;
    }
    validateOutputDirectiveShape(
        code, match, lineIndex, actualCharPos,
        functionName, knownFunction.signature,
        diagnostics, getRuleSeverity
    );
}

export { validateOutputDirective };
