import { existsSync } from 'node:fs';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
let treeSitterModulePromise;
let sharedParser;
const languageCache = new Map();
const queryCache = new Map();
async function loadTreeSitterModule() {
    if (!treeSitterModulePromise) {
        treeSitterModulePromise = import('web-tree-sitter')
            .then(async (module) => {
            await module.Parser.init();
            return module;
        })
            .catch((error) => {
            treeSitterModulePromise = undefined;
            const detail = error instanceof Error ? ` ${error.message}` : '';
            throw new Error(`Failed to load the bundled "web-tree-sitter" dependency.${detail}`);
        });
    }
    return treeSitterModulePromise;
}
export function resolveTreeSitterAsset(grammar, fileName) {
    const here = path.dirname(fileURLToPath(import.meta.url));
    const candidates = [
        path.resolve(here, '../../vendor/tree-sitter', grammar, fileName),
        path.resolve(here, '../../../vendor/tree-sitter', grammar, fileName),
        path.resolve(process.cwd(), 'vendor/tree-sitter', grammar, fileName),
    ];
    return candidates.find(candidate => existsSync(candidate)) ?? null;
}
export async function loadTreeSitterLanguage(grammar) {
    const cached = languageCache.get(grammar);
    if (cached)
        return cached;
    const promise = (async () => {
        const wasmPath = resolveTreeSitterAsset(grammar, 'grammar.wasm');
        if (!wasmPath)
            return null;
        const module = await loadTreeSitterModule();
        try {
            return await module.Language.load(wasmPath);
        }
        catch {
            return null;
        }
    })();
    languageCache.set(grammar, promise);
    return promise;
}
async function loadTreeSitterTagsQuery(grammar) {
    const cached = queryCache.get(grammar);
    if (cached)
        return cached;
    const promise = (async () => {
        const queryPath = resolveTreeSitterAsset(grammar, 'tags.scm');
        if (!queryPath)
            return null;
        const [module, language, source] = await Promise.all([
            loadTreeSitterModule(),
            loadTreeSitterLanguage(grammar),
            readFile(queryPath, 'utf8'),
        ]);
        if (!language || source.trim().length === 0)
            return null;
        try {
            return new module.Query(language, source);
        }
        catch {
            return null;
        }
    })();
    queryCache.set(grammar, promise);
    return promise;
}
export async function parseTreeSitterContent(grammar, content) {
    const language = await loadTreeSitterLanguage(grammar);
    if (!language)
        return null;
    const module = await loadTreeSitterModule();
    // web-tree-sitter Parser/Tree are Emscripten handles that JS GC never frees. A
    // single Parser is reused across files (its grammar is reassigned per call) to
    // avoid per-file allocation. setLanguage + parse run with no intervening await,
    // so concurrent callers cannot observe a mismatched grammar. The returned tree
    // is independent of the parser once produced; the caller owns it and must call
    // tree.delete() after copying out the node data it needs.
    if (!sharedParser) {
        sharedParser = new module.Parser();
    }
    sharedParser.setLanguage(language);
    return sharedParser.parse(content);
}
export async function collectTreeSitterTagCaptures(grammar, root) {
    const query = await loadTreeSitterTagsQuery(grammar);
    if (!query)
        return null;
    try {
        return query.captures(root).map(capture => ({
            name: capture.name,
            node: capture.node,
        }));
    }
    catch {
        return null;
    }
}
export function collectTreeSitterErrorNodes(root) {
    const errors = [];
    const visit = (node) => {
        if (node.type === 'ERROR' || node.isMissing) {
            errors.push({
                type: node.type,
                text: node.text,
                line: node.startPosition.row + 1,
                column: node.startPosition.column + 1,
            });
        }
        for (let index = 0; index < node.childCount; index++) {
            const child = node.child(index);
            if (child)
                visit(child);
        }
    };
    visit(root);
    return errors;
}
//# sourceMappingURL=tree-sitter.js.map