import { basename } from 'node:path';
const NAV = '<!-- atlas: project-map.md · flows.md · commands.md · risks.md · open-gaps.md · conventions.md -->';
export function atlasHeader(inputs, hash8, title) {
    return `<!-- atlas_run input ${hash8} · generated ${inputs.generatedAt} -->\n${NAV}\n\n# ${title}\n`;
}
function purposeLine(area, inputs) {
    const inArea = new Set(area.knowledgeIds);
    const docs = inputs.knowledge
        .filter((k) => inArea.has(k.id) && (k.itemType === 'wiki' || k.itemType === 'spec'))
        .sort((a, b) => b.trustLevel - a.trustLevel);
    const top = docs[0];
    if (top) {
        const text = (top.summary && top.summary.trim()) || top.content.split(/(?<=\.)\s/)[0] || '';
        if (text.trim())
            return text.trim();
    }
    if (area.labels.length > 0) {
        return `labels: ${area.labels.map((l) => `${l.type}/${l.value}`).join(', ')}`;
    }
    return '(no description — see open-gaps.md)';
}
function keyFiles(area, inputs) {
    const counts = new Map();
    for (const p of area.paths)
        counts.set(p, 0);
    const bump = (p) => {
        if (p && counts.has(p))
            counts.set(p, (counts.get(p) ?? 0) + 1);
    };
    for (const k of inputs.knowledge)
        bump(k.metadata.sourcePath);
    for (const a of inputs.atoms)
        bump(a.trigger.files?.[0]);
    return [...counts.entries()]
        .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
        .slice(0, 5)
        .map(([p]) => basename(p));
}
function keySymbols(area, inputs) {
    const inArea = new Set(area.atomIds);
    const freq = new Map();
    for (const a of inputs.atoms) {
        if (!inArea.has(a.id))
            continue;
        for (const s of a.trigger.symbols ?? [])
            freq.set(s, (freq.get(s) ?? 0) + 1);
    }
    const inAreaK = new Set(area.knowledgeIds);
    for (const k of inputs.knowledge) {
        if (!inAreaK.has(k.id))
            continue;
        for (const l of k.labels)
            if (l.type === 'symbol')
                freq.set(l.value, (freq.get(l.value) ?? 0) + 1);
    }
    return [...freq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, 8).map(([s]) => s);
}
export function buildProjectMap(inputs) {
    const lines = ['Entry point for understanding this project. Generated by `tuberosa atlas`.\n'];
    for (const area of inputs.areas) {
        const purpose = purposeLine(area, inputs);
        const files = keyFiles(area, inputs);
        const symbols = keySymbols(area, inputs);
        const deps = inputs.areaDeps.filter((e) => e.from === area.key).map((e) => e.to);
        lines.push(`## ${area.key} — ${area.label}`);
        lines.push(purpose);
        if (files.length)
            lines.push(`key files:   ${files.join(', ')}`);
        if (symbols.length)
            lines.push(`key symbols: ${symbols.join(', ')}`);
        lines.push(`files ${area.counts.files} · knowledge ${area.counts.knowledge} · atoms ${area.counts.atoms} ` +
            `(${area.counts.verifiedAtoms} verified) · crossing edges ${area.crossingRelations}`);
        if (deps.length)
            lines.push(`→ depends on: ${deps.join(', ')}`);
        lines.push('');
    }
    return `${atlasHeader(inputs, 'PENDING', 'Project Map')}\n${lines.join('\n')}`;
}
export function buildFlows(inputs) {
    const lines = ["_Best-effort, derived from Tuberosa's own knowledge graph. See open-gaps.md._\n"];
    lines.push('## Area dependency map');
    if (inputs.areaDeps.length === 0) {
        lines.push('No cross-area dependencies inferred yet.');
    }
    else {
        for (const e of inputs.areaDeps)
            lines.push(`- ${e.from} → ${e.to} (${e.weight})`);
    }
    lines.push('', '## Co-change coupling');
    const coChange = inputs.relations.filter((r) => r.relationType === 'co_changes_with');
    if (coChange.length === 0) {
        lines.push('No co-change data — run `pnpm run infer-co-change` to populate temporal coupling.');
    }
    else {
        lines.push(`${coChange.length} co-change relations recorded.`);
    }
    return `${atlasHeader(inputs, 'PENDING', 'Flows')}\n${lines.join('\n')}`;
}
const COMMAND_GROUPS = [
    { title: 'Build & Dev', match: (n) => /^(build|dev|start|workbench)/.test(n) },
    { title: 'Test & Eval', match: (n) => /^(test|eval:|sandbox|benchmark|calibrate-fusion)/.test(n) },
    {
        title: 'Data & Maintenance',
        match: (n) => /^(backfill|archival-sweep|infer-co-change|prune-stale-edges|cluster-|migrate|seed|import:docs|backup|restore)/.test(n),
    },
    { title: 'Packs', match: (n) => /^(export-pack|import-pack)$/.test(n) },
    { title: 'Ops', match: (n) => /^(mcp|worker|error-logs|organization|context-quality)$/.test(n) },
];
export function buildCommands(inputs) {
    const names = Object.keys(inputs.scripts).sort();
    const used = new Set();
    const lines = [];
    for (const group of COMMAND_GROUPS) {
        const members = names.filter((n) => !used.has(n) && group.match(n));
        if (members.length === 0)
            continue;
        members.forEach((n) => used.add(n));
        lines.push(`### ${group.title}`);
        for (const n of members)
            lines.push(`- \`pnpm run ${n}\` — \`${inputs.scripts[n]}\``);
        lines.push('');
    }
    const other = names.filter((n) => !used.has(n));
    if (other.length) {
        lines.push('### Other');
        for (const n of other)
            lines.push(`- \`pnpm run ${n}\` — \`${inputs.scripts[n]}\``);
        lines.push('');
    }
    if (inputs.readmeCommands) {
        lines.push('### From README', '', inputs.readmeCommands);
    }
    return `${atlasHeader(inputs, 'PENDING', 'Commands')}\n${lines.join('\n')}`;
}
export function buildRisks(inputs) {
    const lines = [];
    const gotchas = inputs.atoms
        .filter((a) => a.type === 'gotcha')
        .sort((a, b) => a.tier.localeCompare(b.tier) || a.claim.localeCompare(b.claim));
    lines.push('## Gotchas');
    if (gotchas.length === 0)
        lines.push('None recorded.');
    for (const g of gotchas) {
        lines.push(`- ${g.claim}`);
        for (const p of g.pitfalls ?? [])
            lines.push(`  - pitfall: ${p}`);
    }
    const staleStatuses = new Set(['changed', 'missing']);
    const stalePaths = new Set(inputs.ledger.filter((f) => staleStatuses.has(f.status)).map((f) => f.path));
    const stale = inputs.knowledge.filter((k) => {
        const p = k.metadata.sourcePath;
        return p ? stalePaths.has(p) : false;
    });
    lines.push('', '## Stale knowledge');
    if (stale.length === 0)
        lines.push('None — all knowledge maps to tracked files.');
    for (const k of stale)
        lines.push(`- ${k.metadata.sourcePath} — ${k.title}`);
    lines.push('', '## Open conflicts');
    lines.push(`${inputs.openConflictCount} open import conflict(s).`);
    return `${atlasHeader(inputs, 'PENDING', 'Risks')}\n${lines.join('\n')}`;
}
const CONVENTION_SCOPES = [
    { scope: 'project', label: 'Project' },
    { scope: 'team', label: 'Team' },
    { scope: 'user', label: 'Personal' },
];
export function buildConventions(inputs) {
    const conventions = inputs.atoms
        .filter((a) => a.type === 'convention' && a.status === 'active')
        .sort((a, b) => a.id.localeCompare(b.id));
    if (conventions.length === 0) {
        return `${atlasHeader(inputs, 'PENDING', 'Conventions')}\nNo conventions captured yet — run \`tuberosa_bootstrap_handbook\` to capture them.`;
    }
    const lines = [
        'Active conventions for this project, grouped by scope. Generated by `tuberosa atlas`.\n',
    ];
    for (const { scope, label } of CONVENTION_SCOPES) {
        const inScope = conventions.filter((a) => a.scope === scope);
        if (inScope.length === 0)
            continue;
        lines.push(`## ${label}`);
        const byCategory = new Map();
        for (const a of inScope) {
            const category = String(a.metadata?.category ?? 'other');
            const bucket = byCategory.get(category);
            if (bucket)
                bucket.push(a);
            else
                byCategory.set(category, [a]);
        }
        for (const category of [...byCategory.keys()].sort((x, y) => x.localeCompare(y))) {
            lines.push(`### ${category}`);
            for (const a of byCategory.get(category) ?? []) {
                const meta = (a.metadata ?? {});
                lines.push(`- ${a.claim} _(tier: ${a.tier})_`);
                if (typeof meta.author === 'string' && meta.author.trim()) {
                    lines.push(`  - author: ${meta.author}`);
                }
                if (Array.isArray(meta.steps)) {
                    meta.steps.forEach((step, idx) => lines.push(`  ${idx + 1}. ${String(step)}`));
                }
            }
            lines.push('');
        }
    }
    return `${atlasHeader(inputs, 'PENDING', 'Conventions')}\n${lines.join('\n')}`;
}
export function buildOpenGaps(inputs) {
    const lines = [];
    const undocumented = inputs.areas.filter((a) => purposeLine(a, inputs) === '(no description — see open-gaps.md)');
    lines.push('## Undocumented areas');
    if (undocumented.length === 0)
        lines.push('None.');
    for (const a of undocumented)
        lines.push(`- ${a.key}`);
    const thin = inputs.areas.filter((a) => a.counts.files > 0 && (a.counts.verifiedAtoms === 0 || a.counts.knowledge === 0));
    lines.push('', '## Thin coverage');
    if (thin.length === 0)
        lines.push('None.');
    for (const a of thin) {
        lines.push(`- ${a.key} — knowledge ${a.counts.knowledge}, verified atoms ${a.counts.verifiedAtoms}`);
    }
    lines.push('', '## Recorded knowledge gaps');
    if (inputs.knowledgeGaps.length === 0)
        lines.push('None.');
    for (const g of inputs.knowledgeGaps)
        lines.push(`- ${g.prompt}`);
    const unverified = inputs.atoms.filter((a) => !a.verification?.command);
    lines.push('', '## Unverified atoms');
    if (unverified.length === 0)
        lines.push('None.');
    for (const a of unverified)
        lines.push(`- ${a.claim}`);
    return `${atlasHeader(inputs, 'PENDING', 'Open Gaps')}\n${lines.join('\n')}`;
}
