/**
 * `dql verify` — prove the on-disk manifest is reproducible from source.
 *
 * Recompiles the manifest in-memory and diffs it against `dql-manifest.json`.
 * Returns a non-zero exit code (and prints a diff summary) on drift. Used by
 * CI to ensure programmable artifacts (Apps, dashboards, blocks, RLS rules,
 * schedules) stay in lock-step with their source files.
 */
import { existsSync, readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { buildManifest, resolveDataLexManifestPath, resolveDbtManifestPath } from '@duckcodeailabs/dql-core';
export async function runVerify(pathArg, rest, flags) {
    const allArgs = [...(pathArg ? [pathArg] : []), ...rest];
    let dbtManifestPath;
    const dbtIdx = allArgs.indexOf('--dbt-manifest');
    if (dbtIdx >= 0 && allArgs[dbtIdx + 1]) {
        dbtManifestPath = resolve(allArgs[dbtIdx + 1]);
        if (!existsSync(dbtManifestPath)) {
            throw new Error(`dbt manifest not found: ${dbtManifestPath}`);
        }
    }
    let maxDbtHops;
    const hopsIdx = allArgs.indexOf('--dbt-hops');
    if (hopsIdx >= 0 && allArgs[hopsIdx + 1]) {
        const parsed = Number.parseInt(allArgs[hopsIdx + 1], 10);
        if (Number.isFinite(parsed) && parsed > 0)
            maxDbtHops = parsed;
    }
    const pathCandidates = allArgs.filter((a) => !a.startsWith('-'));
    const filteredCandidates = pathCandidates
        .filter((c) => dbtIdx < 0 || c !== allArgs[dbtIdx + 1])
        .filter((c) => hopsIdx < 0 || c !== allArgs[hopsIdx + 1]);
    const projectRoot = resolve(filteredCandidates[0] ?? '.');
    const manifestPath = join(projectRoot, 'dql-manifest.json');
    if (!existsSync(manifestPath)) {
        throw new Error(`dql-manifest.json not found at ${manifestPath}. Run \`dql compile\` first.`);
    }
    let dqlVersion = '0.6.0';
    try {
        const pkgPath = join(import.meta.dirname ?? __dirname, '..', '..', 'package.json');
        if (existsSync(pkgPath)) {
            dqlVersion = JSON.parse(readFileSync(pkgPath, 'utf-8')).version ?? dqlVersion;
        }
    }
    catch { /* use default */ }
    const resolvedDbt = resolveDbtManifestPath(projectRoot, dbtManifestPath);
    if (resolvedDbt)
        dbtManifestPath = resolvedDbt;
    const datalexManifestPath = resolveDataLexManifestPath(projectRoot, flags.datalexManifestPath || undefined) ?? undefined;
    if (flags.datalexManifestPath && (!datalexManifestPath || !existsSync(datalexManifestPath))) {
        throw new Error(`DataLex manifest not found: ${flags.datalexManifestPath}`);
    }
    const onDisk = JSON.parse(readFileSync(manifestPath, 'utf-8'));
    const fresh = buildManifest({ projectRoot, dqlVersion, dbtManifestPath, maxDbtHops, datalexManifestPath });
    const drift = diffManifest(onDisk, fresh);
    const json = flags.format === 'json';
    if (drift.length === 0) {
        if (json) {
            console.log(JSON.stringify({ ok: true }));
        }
        else {
            console.log('  ✓ Manifest verified — no drift between source tree and dql-manifest.json.');
        }
        return;
    }
    if (json) {
        console.log(JSON.stringify({ ok: false, drift }, null, 2));
    }
    else {
        console.error(`  ✗ Manifest drift detected:`);
        for (const item of drift)
            console.error(`    - ${item}`);
        console.error('\n  Run `dql compile` to regenerate, then commit.');
    }
    process.exit(1);
}
function diffManifest(a, b) {
    const drift = [];
    diffMap('blocks', new Set(Object.keys(a.blocks)), new Set(Object.keys(b.blocks)), drift);
    diffMap('notebooks', new Set(Object.keys(a.notebooks)), new Set(Object.keys(b.notebooks)), drift);
    diffMap('apps', new Set(Object.keys(a.apps ?? {})), new Set(Object.keys(b.apps ?? {})), drift);
    diffMap('dashboards', new Set(Object.keys(a.dashboards ?? {})), new Set(Object.keys(b.dashboards ?? {})), drift);
    diffMap('metrics', new Set(Object.keys(a.metrics)), new Set(Object.keys(b.metrics)), drift);
    diffMap('dimensions', new Set(Object.keys(a.dimensions)), new Set(Object.keys(b.dimensions)), drift);
    diffMap('sources', new Set(Object.keys(a.sources)), new Set(Object.keys(b.sources)), drift);
    // Block-level drift: shape changes that matter for downstream readers.
    for (const [name, block] of Object.entries(a.blocks)) {
        const other = b.blocks[name];
        if (!other)
            continue;
        if (block.sql !== other.sql)
            drift.push(`blocks/${name}: SQL drift`);
        if ((block.status ?? '') !== (other.status ?? ''))
            drift.push(`blocks/${name}: status drift (${block.status} → ${other.status})`);
        if ((block.domain ?? '') !== (other.domain ?? ''))
            drift.push(`blocks/${name}: domain drift`);
    }
    return drift;
}
function diffMap(label, a, b, into) {
    for (const k of a)
        if (!b.has(k))
            into.push(`${label}: removed "${k}"`);
    for (const k of b)
        if (!a.has(k))
            into.push(`${label}: added "${k}"`);
}
//# sourceMappingURL=verify.js.map