import { DiagnosticSeverity, } from "vscode-languageserver-protocol";
import { parseAgency } from "../parser.js";
import { resolveImports } from "../preprocessors/importResolver.js";
import { resolveReExports } from "../preprocessors/resolveReExports.js";
import { buildCompilationUnit } from "../compilationUnit.js";
import { typeCheck } from "../typeChecker/index.js";
import { buildSemanticIndex } from "./semantics.js";
import { resolveAgencyImportPath } from "../importPaths.js";
// Names auto-imported from std::index by the parser template. Kept in sync
// with lib/templates/backends/agency/template.mustache.
const STDLIB_AUTO_IMPORTS = [
    "print", "printJSON", "parseJSON", "input", "sleep", "round",
    "read", "write", "readImage", "notify", "range",
    "mostCommon", "keys", "values", "entries", "emit",
];
/**
 * Inject a synthetic `import { ... } from "std::index"` so the LSP sees the
 * same auto-imports the CLI parser template prepends — see
 * lib/templates/backends/agency/template.mustache. The synthetic is added
 * unconditionally (alongside any user `import … from "std::index"`) so a
 * user who imports a *subset* like `import { range } from "std::index"`
 * still gets `print`, `read`, etc. from the auto-imports — matching CLI
 * behavior where the template prepends a separate fixed import line.
 *
 * Skipped when the SymbolTable doesn't have std::index loaded — that's
 * the test-with-empty-SymbolTable case and synthesizing here would just
 * produce downstream "Symbol 'print' is not defined" noise.
 */
function ensureStdlibImport(program, symbolTable, fsPath) {
    let stdlibPath;
    try {
        stdlibPath = resolveAgencyImportPath("std::index", fsPath);
    }
    catch {
        return program;
    }
    if (!symbolTable.has(stdlibPath))
        return program;
    const synthetic = {
        type: "importStatement",
        importedNames: [
            {
                type: "namedImport",
                importedNames: STDLIB_AUTO_IMPORTS,
                safeNames: [],
                aliases: {},
            },
        ],
        modulePath: "std::index",
        isAgencyImport: true,
    };
    return { ...program, nodes: [synthetic, ...program.nodes] };
}
export function runDiagnostics(doc, fsPath, config, symbolTable) {
    const source = doc.getText();
    const diagnostics = [];
    const parseResult = parseAgency(source, config, false);
    if (!parseResult.success) {
        const ed = parseResult.errorData;
        if (ed) {
            diagnostics.push({
                severity: DiagnosticSeverity.Error,
                range: {
                    start: { line: ed.line, character: ed.column },
                    end: { line: ed.line, character: ed.column + (ed.length || 1) },
                },
                message: ed.message,
                source: "agency",
            });
        }
        else {
            diagnostics.push({
                severity: DiagnosticSeverity.Error,
                range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
                message: parseResult.message ?? "Parse error",
                source: "agency",
            });
        }
        return { diagnostics, program: null, info: null, semanticIndex: {}, scopes: [] };
    }
    let program = parseResult.result;
    // The CLI parses source through a template that auto-injects an
    // `import { ... } from "std::index"` statement. The LSP path uses
    // `applyTemplate: false` so editor positions match the user's source —
    // but that means stdlib calls (`print`, `read`, …) would resolve as
    // undefined here. Synthesize the same import so they resolve through
    // `importedFunctions` like in the CLI flow.
    program = ensureStdlibImport(program, symbolTable, fsPath);
    try {
        program = resolveReExports(program, symbolTable, fsPath);
        program = resolveImports(program, symbolTable, fsPath);
    }
    catch (err) {
        diagnostics.push({
            severity: DiagnosticSeverity.Error,
            range: { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } },
            message: err instanceof Error ? err.message : String(err),
            source: "agency",
        });
        return { diagnostics, program: null, info: null, semanticIndex: {}, scopes: [] };
    }
    const info = buildCompilationUnit(program, symbolTable, fsPath, source);
    const { errors, scopes, interruptKindsByFunction } = typeCheck(program, config, info);
    for (const err of errors) {
        const range = err.loc
            ? {
                start: { line: err.loc.line, character: err.loc.col },
                end: { line: err.loc.line, character: err.loc.col },
            }
            : { start: { line: 0, character: 0 }, end: { line: 0, character: 1 } };
        diagnostics.push({
            severity: DiagnosticSeverity.Error,
            range,
            message: err.message,
            source: "agency",
        });
    }
    return {
        diagnostics,
        program,
        info,
        semanticIndex: buildSemanticIndex(program, fsPath, symbolTable, interruptKindsByFunction),
        scopes,
    };
}
