/**
 * commands/docs-audit.mjs — CLI entry point for the docs-coherence audit (C33).
 *
 * Used by:
 *   - `/apt:review` Pass 6 (spec §In scope #3)
 *   - Standalone debugging (`apt-tools docs-audit .`)
 *   - CI pre-commit (`docs-audit . --format json | jq '.findings | length'`)
 *
 * Flags:
 *   --diff-files <csv>    explicit CSV of diff files (deleted/renamed inferred from git)
 *   --since <ref>         compute diff from <ref>..HEAD via `git diff --name-status`
 *   --dry-run             skip git invocations entirely (empty diffFacts)
 *   --policy <path>       path to an override policy JSON (testing hook)
 *   --format json|md      output format (default json)
 */
import { execFileSync } from 'node:child_process';
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { auditDocsDrift } from '../docs-audit/audit.mjs';
import { loadDocsPolicy } from '../docs-audit/policy.mjs';
import { parseFlags } from '../util/args.mjs';
import { err, ok, UsageError } from '../util/result.mjs';
/**
 * Sentinel error class for helper-level usage failures inside cmdDocsAudit.
 * The dispatcher catches it and converts it into the standard err() envelope.
 */
class DocsAuditUsageError extends UsageError {
    constructor(message) {
        super(message);
        this.name = 'DocsAuditUsageError';
    }
}
export async function cmdDocsAudit(projectDir, extraArgs) {
    if (!projectDir)
        return err('Usage: apt-tools docs-audit <project-dir> [flags]');
    const root = resolve(projectDir);
    const flags = parseFlags(extraArgs);
    const format = (flags.get('format') || 'json').toLowerCase();
    let policy;
    try {
        policy = resolvePolicy(root, flags);
    }
    catch (e) {
        if (e instanceof DocsAuditUsageError)
            return err(e.message);
        throw e;
    }
    const diffFacts = resolveDiffFacts(root, flags);
    const result = await auditDocsDrift({ projectDir: root, policy, diffFacts });
    const payload = {
        status: 'ok',
        command: 'docs-audit',
        findings: result.findings,
        patch_plan: result.patch_plan,
        dropped: result.dropped,
    };
    if (format === 'md') {
        payload.markdown = renderMarkdown(result);
    }
    return ok(payload);
}
function resolvePolicy(root, flags) {
    const overridePath = flags.get('policy');
    if (overridePath) {
        try {
            const raw = JSON.parse(readFileSync(resolve(overridePath), 'utf-8'));
            return raw;
        }
        catch (e) {
            throw new DocsAuditUsageError(`docs-audit: could not load --policy from ${overridePath}: ${e.message}`);
        }
    }
    return loadDocsPolicy(root);
}
function resolveDiffFacts(root, flags) {
    if (flags.has('dry-run')) {
        return { deletedPaths: [], renamedPaths: [], addedExports: [], removedExports: [] };
    }
    const explicit = flags.get('diff-files');
    if (explicit && typeof explicit === 'string') {
        // Treat each entry as a candidate deleted path. Without git access we
        // can't distinguish deletions from mere changes, but the grep engine
        // only false-positives on benign in-prose mentions — it still succeeds.
        return {
            deletedPaths: explicit
                .split(',')
                .map((s) => s.trim())
                .filter(Boolean),
            renamedPaths: [],
            addedExports: [],
            removedExports: [],
        };
    }
    if (!existsSync(resolve(root, '.git'))) {
        return { deletedPaths: [], renamedPaths: [], addedExports: [], removedExports: [] };
    }
    try {
        const since = flags.get('since');
        const args = since
            ? ['diff', '--name-status', `${since}..HEAD`]
            : ['diff', 'HEAD', '--name-status'];
        const out = execFileSync('git', args, {
            cwd: root,
            encoding: 'utf-8',
            stdio: ['ignore', 'pipe', 'ignore'],
        });
        return parseNameStatus(out);
    }
    catch {
        return { deletedPaths: [], renamedPaths: [], addedExports: [], removedExports: [] };
    }
}
function parseNameStatus(raw) {
    const deletedPaths = [];
    const renamedPaths = [];
    for (const line of raw.split('\n')) {
        const t = line.trim();
        if (!t)
            continue;
        // Format: "D\tpath"   or   "R100\told\tnew"
        const parts = t.split('\t').filter(Boolean);
        const code = parts[0];
        if (code?.startsWith('D') && parts[1]) {
            deletedPaths.push(parts[1]);
        }
        else if (code?.startsWith('R') && parts[1] && parts[2]) {
            renamedPaths.push({ from: parts[1], to: parts[2] });
        }
    }
    return { deletedPaths, renamedPaths, addedExports: [], removedExports: [] };
}
function renderMarkdown(result) {
    const out = ['### Docs-coherence audit', ''];
    if (result.findings.length === 0 && result.patch_plan.length === 0) {
        out.push('_No docs-drift detected._');
        return out.join('\n');
    }
    if (result.findings.length > 0) {
        out.push('**Findings (flag-drift):**');
        for (const f of result.findings) {
            out.push(`- \`${f.file}:${f.line}\` — ${f.stale_claim} (${f.confidence})` +
                (f.suggested_rewrite ? ` → \`${f.suggested_rewrite}\`` : ''));
        }
    }
    if (result.patch_plan.length > 0) {
        out.push('', '**Patch plan (auto-update):**');
        for (const p of result.patch_plan) {
            out.push(`- \`${p.file}:${p.line}\` — ${p.stale_claim}`);
        }
    }
    return out.join('\n');
}
//# sourceMappingURL=docs-audit.mjs.map