import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { resolveTreeSitterAsset } from '../tree-sitter.js';
export const ENTITY_CST_PROOF = 'entity-cst-v2';
export const ENTITY_SIGNATURE_PROOF = 'entity-signature-cst-v2';
export const WHOLE_DIFF_GRAMMAR_MANIFEST_VERSION = 1;
/**
 * The grammar bytes and comment policy are part of every CST proof. Keeping
 * their digests checked in makes a replaced WASM file an evidence failure,
 * rather than silently changing what an existing proof name means.
 */
export const WHOLE_DIFF_GRAMMAR_MANIFEST = Object.freeze([
    {
        grammar: 'c',
        extensions: ['c', 'h'],
        commentNodeTypes: ['comment'],
        wasmSha256: 'f0c308e7465d6af2e1de71426afbcb22ba33390227db950652808d248da03bf3',
    },
    {
        grammar: 'c-sharp',
        extensions: ['cs'],
        commentNodeTypes: ['comment'],
        wasmSha256: 'fbb5de1f3c277c9a60fffd8ae485e231babda452ca788d301952e41d4c1a5888',
    },
    {
        grammar: 'cpp',
        extensions: ['cc', 'cpp', 'cxx', 'hh', 'hpp', 'hxx'],
        commentNodeTypes: ['comment'],
        wasmSha256: '212ed7c04c7a6e25702edf3148c2e65a096193fd8154f77a1c1ad1b940216d08',
    },
    {
        grammar: 'go',
        extensions: ['go'],
        commentNodeTypes: ['comment'],
        wasmSha256: '7ca9e17e2b4fe236cb1d2386b0dfa897d7f13016cb88c1a5c8c1140719e5e514',
    },
    {
        grammar: 'java',
        extensions: ['java'],
        commentNodeTypes: ['line_comment', 'block_comment'],
        wasmSha256: '84e70408b5f5e693f77768930ee0f56dd1a21f40b4bd63b16c39d477fceea60c',
    },
    {
        grammar: 'javascript',
        extensions: ['cjs', 'es6', 'js', 'jsx', 'mjs'],
        commentNodeTypes: ['comment'],
        wasmSha256: '5fb488d0cabb4775a594bab85682de5ad6ce83c0d6ac997a9f82dd084d571240',
    },
    {
        grammar: 'kotlin',
        extensions: ['kt', 'kts'],
        commentNodeTypes: ['line_comment', 'block_comment'],
        wasmSha256: '7294358f85c60e15602fc35b175c9b5559ca8bf39aeb0fd91a7d84f63f5fbf1a',
    },
    {
        grammar: 'php',
        extensions: ['php', 'phtml'],
        commentNodeTypes: ['comment'],
        wasmSha256: '3e95620047070f70ecb900fb003a8e60d61cb32e7f3433f856f1e9183f872b9e',
    },
    {
        grammar: 'python',
        extensions: ['py'],
        commentNodeTypes: ['comment'],
        wasmSha256: '36d25eee776af6293dc67bdf6f8f820af6a26bbc49e335cc4821dfd5a0dac685',
    },
    {
        grammar: 'ruby',
        extensions: ['rb'],
        commentNodeTypes: ['comment'],
        wasmSha256: 'b98cfd3724adf318f7ebfb979f167c3b7470cb143384b51ac1fd3f47f9ba4039',
    },
    {
        grammar: 'rust',
        extensions: ['rs'],
        commentNodeTypes: ['line_comment', 'block_comment'],
        wasmSha256: 'a96b69c6fdf9d75b7df225af7e3544443cbd91c518dff3d7864bbef2463f57f0',
    },
    {
        grammar: 'scala',
        extensions: ['sbt', 'sc', 'scala'],
        commentNodeTypes: ['comment', 'block_comment'],
        wasmSha256: '026c2f9a8374109861f6621f4759ef690faebcaa67c2d56b06af3786c206b030',
    },
    {
        grammar: 'swift',
        extensions: ['swift'],
        commentNodeTypes: ['comment', 'multiline_comment'],
        wasmSha256: 'd084eceba50e9a83319f5d171051b53179bd22b6ea6419014900c602d52cfa28',
    },
    {
        grammar: 'tsx',
        extensions: ['tsx'],
        commentNodeTypes: ['comment'],
        wasmSha256: '79e5da75ea62855a0cd67177685f0164eac87d5f630b3cbe1e0a099751ad30f8',
    },
    {
        grammar: 'typescript',
        extensions: ['cts', 'mts', 'ts'],
        commentNodeTypes: ['comment'],
        wasmSha256: '778025db5a8be0e70f8ccc3671e486dfeddd048c25d9e8a70c26de2e1bf6f97d',
    },
]);
for (const entry of WHOLE_DIFF_GRAMMAR_MANIFEST) {
    Object.freeze(entry.extensions);
    Object.freeze(entry.commentNodeTypes);
    Object.freeze(entry);
}
const BY_GRAMMAR = new Map(WHOLE_DIFF_GRAMMAR_MANIFEST.map(entry => [entry.grammar, entry]));
const BY_EXTENSION = new Map(WHOLE_DIFF_GRAMMAR_MANIFEST.flatMap(entry => entry.extensions.map(extension => [extension, entry])));
const canonicalManifest = JSON.stringify({
    version: WHOLE_DIFF_GRAMMAR_MANIFEST_VERSION,
    grammars: WHOLE_DIFF_GRAMMAR_MANIFEST,
});
const computedManifestDigest = createHash('sha256').update(canonicalManifest, 'utf8').digest('hex');
/** Update deliberately with the manifest; a stale edit fails at module load. */
export const WHOLE_DIFF_GRAMMAR_MANIFEST_DIGEST = '61641a2d1ff140f2ec68fc8a20149eac7df471eecbcccafe4a7ea4e50c6de04d';
if (computedManifestDigest !== WHOLE_DIFF_GRAMMAR_MANIFEST_DIGEST) {
    throw new Error(`Entity grammar manifest digest mismatch: expected ${WHOLE_DIFF_GRAMMAR_MANIFEST_DIGEST}, computed ${computedManifestDigest}.`);
}
export function grammarManifestEntry(grammar) {
    return BY_GRAMMAR.get(grammar);
}
export function grammarManifestEntryForPath(relativePath) {
    const extension = path.posix.extname(relativePath.replaceAll('\\', '/')).slice(1).toLowerCase();
    return BY_EXTENSION.get(extension);
}
const VUE_GRAMMAR_CANDIDATES = Object.freeze(['javascript', 'typescript', 'tsx']
    .map(grammar => grammarManifestEntry(grammar))
    .filter((entry) => entry !== undefined));
/**
 * Every grammar a path can select without reading its contents.
 *
 * Vue chooses JavaScript, TypeScript, or TSX from its script blocks, so its
 * bounded candidate set is verified up front. Ordinary extensions have one
 * exact grammar and unsupported/data formats have none.
 */
export function grammarManifestCandidatesForPath(relativePath) {
    const normalized = relativePath.replaceAll('\\', '/');
    if (path.posix.extname(normalized).toLowerCase() === '.vue')
        return VUE_GRAMMAR_CANDIDATES;
    const entry = grammarManifestEntryForPath(normalized);
    return entry ? [entry] : [];
}
const verificationCache = new Map();
const verificationResults = new Map();
function verificationKey(entry) {
    return `${entry.grammar}\u0000${entry.wasmSha256}`;
}
/**
 * Whether this process has physically re-verified the checked-in bytes for a
 * grammar. This synchronous admission predicate deliberately returns false
 * while verification is absent, pending, failed, or addressed to another
 * digest.
 */
export function isGrammarVerified(grammar) {
    const entry = grammarManifestEntry(grammar);
    return entry !== undefined && verificationResults.get(verificationKey(entry)) === true;
}
/** Verify the physical grammar once per process before admitting CST evidence. */
export function verifyGrammarManifestEntry(entry) {
    const cacheKey = verificationKey(entry);
    const cached = verificationCache.get(cacheKey);
    if (cached)
        return cached;
    const pending = (async () => {
        const asset = resolveTreeSitterAsset(entry.grammar, 'grammar.wasm');
        if (!asset)
            return false;
        try {
            const bytes = await readFile(asset);
            return createHash('sha256').update(bytes).digest('hex') === entry.wasmSha256;
        }
        catch {
            return false;
        }
    })().then(verified => {
        verificationResults.set(cacheKey, verified);
        return verified;
    });
    verificationCache.set(cacheKey, pending);
    return pending;
}
//# sourceMappingURL=grammar-manifest.js.map