import Table from 'cli-table3';
import chalk from 'chalk';
import { formatNumber, formatPayloadJson } from '../utils.js';
import { sectionHeader, stripAnsi, tableChars } from '../output/tabular.js';
import { formatRepoMapMarkdown as renderRepoMapMarkdown, formatRepoMapPlain as renderRepoMapPlain, formatRepoMapXml as renderRepoMapXml, renderRepoMap as renderBudgetedRepoMap, } from './map.js';
import { renderCodeDiffJson, renderCodeDiffText } from './diff.js';
import { directoryTreeFooter, directoryTreeHeader } from './tree.js';
import { renderInlineSourceBlock } from './source-inline.js';
const colors = {
    header: value => chalk.bold(value),
    key: value => chalk.cyan(value),
    value: value => value,
    dim: value => chalk.dim(value),
    ok: value => chalk.green(value),
    warn: value => chalk.yellow(value),
    bad: value => chalk.red(value),
};
const noColor = Object.fromEntries(Object.keys(colors).map(key => [key, (value) => value]));
function palette(ci = false) {
    return ci ? noColor : colors;
}
function addSeparators(rendered, ci = false) {
    void ci;
    return rendered;
}
function tableTitle(title, body, ci = false) {
    const width = stripAnsi(body.split('\n')[0] ?? '').length;
    return `${palette(ci).header(sectionHeader(title, width, ci))}\n${body}`;
}
function formatLocation(node) {
    return node.line ? `${node.relativePath ?? ''}:${node.line}` : (node.relativePath ?? '');
}
function appendInlineContexts(base, projections) {
    const rendered = [];
    for (const projection of projections) {
        if (projection.inlineContext) {
            const block = projection.inlineContext;
            rendered.push(`${block.relativePath}:${block.startLine}-${block.endLine}`);
            rendered.push(renderInlineSourceBlock(block));
        }
        if (projection.inlineContextDiagnostic) {
            rendered.push(`note: [${projection.inlineContextDiagnostic.code}] ${projection.inlineContextDiagnostic.message}`);
        }
    }
    return rendered.length === 0 ? base : `${base.trimEnd()}\n\nInline context:\n${rendered.join('\n')}\n`;
}
function formatResolution(status, c) {
    if (status === 'resolved')
        return c.ok(status);
    if (status === 'ambiguous')
        return c.warn(status);
    return c.bad(status);
}
function formatCandidate(candidate) {
    const location = candidate.relativePath
        ? `${candidate.relativePath}${candidate.line ? `:${candidate.line}` : ''}`
        : candidate.name;
    return location;
}
function formatRelationDetail(relation) {
    const parts = [];
    if (relation.edge.detail)
        parts.push(relation.edge.detail);
    if (relation.edge.specifier)
        parts.push(relation.edge.specifier);
    if (relation.edge.resolutionBasis)
        parts.push(`basis: ${relation.edge.resolutionBasis}`);
    if (relation.edge.candidates && relation.edge.candidates.length > 0) {
        const renderedCandidates = relation.edge.candidates.slice(0, 2).map(formatCandidate);
        const suffix = relation.edge.candidates.length > 2 ? ` +${relation.edge.candidates.length - 2} more` : '';
        parts.push(`${relation.edge.candidates.length} candidates: ${renderedCandidates.join(', ')}${suffix}`);
    }
    return parts.join(' | ');
}
export { formatPayloadJson };
export function formatCodeDiffJson(result, pretty = false) {
    return renderCodeDiffJson(result, pretty);
}
export function formatDirectoryTreeJson(result, pretty = false) {
    return formatPayloadJson(result, pretty);
}
export function formatDirectoryTree(result) {
    const lines = [directoryTreeHeader(result), ...result.lines];
    const footer = directoryTreeFooter(result);
    if (footer)
        lines.push(footer);
    return `${lines.join('\n')}\n`;
}
export function formatCodeDiff(result, ci = false) {
    void ci;
    return renderCodeDiffText(result);
}
export function formatSearchResults(title, results, ci = false) {
    const c = palette(ci);
    if (results.length === 0)
        return `${c.dim('No matches found.')}\n`;
    const table = new Table({
        head: [c.key('Name'), c.key('Type'), c.key('Location'), c.key('Container')],
        chars: tableChars(ci),
        style: { head: [], border: [] },
    });
    for (const result of results) {
        table.push([
            c.value(result.node.name),
            result.node.type,
            formatLocation(result.node),
            result.node.containerName ?? '',
        ]);
    }
    return appendInlineContexts(`${tableTitle(title, addSeparators(table.toString(), ci), ci)}\n`, results);
}
export function formatContentResults(results, ci = false) {
    const c = palette(ci);
    if (results.length === 0)
        return `${c.dim('No content matches found.')}\n`;
    const table = new Table({
        head: [c.key('Location'), c.key('Language'), c.key('Snippet')],
        chars: tableChars(ci),
        style: { head: [], border: [] },
    });
    for (const result of results) {
        table.push([`${result.relativePath}:${result.line}`, result.language, result.snippet]);
    }
    return `${tableTitle('Code Content', addSeparators(table.toString(), ci), ci)}\n`;
}
export function formatRelationResults(title, label, relations, ci = false, side = 'to') {
    const c = palette(ci);
    if (relations.length === 0)
        return `${c.dim('No relationships found.')}\n`;
    const table = new Table({
        head: [c.key(label), c.key('Location'), c.key('Resolution'), c.key('Detail')],
        chars: tableChars(ci),
        style: { head: [], border: [] },
    });
    for (const relation of relations) {
        const target = side === 'from' ? relation.from : relation.to;
        table.push([
            target?.name ?? relation.edge.targetName ?? '(unresolved)',
            target ? formatLocation(target) : (relation.edge.toPath ?? ''),
            formatResolution(relation.edge.status, c),
            formatRelationDetail(relation),
        ]);
    }
    return appendInlineContexts(`${tableTitle(title, addSeparators(table.toString(), ci), ci)}\n`, relations);
}
export function formatDependencies(result, ci = false) {
    const c = palette(ci);
    const sections = [];
    sections.push(c.header(`Repository: ${result.targetFile?.relativePath ?? result.target}`));
    if (result.importers.length > 0) {
        const table = new Table({
            head: [c.key('Importer'), c.key('Resolution'), c.key('Specifier')],
            chars: tableChars(ci),
            style: { head: [], border: [] },
        });
        for (const importer of result.importers) {
            table.push([
                importer.from.relativePath ?? importer.from.name,
                formatResolution(importer.edge.status, c),
                importer.edge.specifier ?? importer.edge.targetName ?? '',
            ]);
        }
        sections.push(tableTitle('Importers', addSeparators(table.toString(), ci), ci));
    }
    else {
        sections.push(c.dim('No importers found.'));
    }
    if (result.localImports.length > 0) {
        const table = new Table({
            head: [c.key('Local Module'), c.key('Resolution'), c.key('Specifier')],
            chars: tableChars(ci),
            style: { head: [], border: [] },
        });
        for (const imported of result.localImports) {
            table.push([
                imported.to?.name ?? imported.edge.targetName ?? '(unresolved)',
                formatResolution(imported.edge.status, c),
                imported.edge.specifier ?? imported.edge.targetName ?? '',
            ]);
        }
        sections.push(tableTitle('Local Imports', addSeparators(table.toString(), ci), ci));
    }
    if (result.externalImports.length > 0) {
        const table = new Table({
            head: [c.key('External Package'), c.key('Resolution'), c.key('Specifier')],
            chars: tableChars(ci),
            style: { head: [], border: [] },
        });
        for (const imported of result.externalImports) {
            table.push([
                imported.to?.name ?? imported.edge.targetName ?? '(unresolved)',
                formatResolution(imported.edge.status, c),
                imported.edge.specifier ?? imported.edge.targetName ?? '',
            ]);
        }
        sections.push(tableTitle('External Imports', addSeparators(table.toString(), ci), ci));
    }
    if (result.unresolvedImports.length > 0) {
        const table = new Table({
            head: [c.key('Unresolved Import'), c.key('Resolution'), c.key('Specifier')],
            chars: tableChars(ci),
            style: { head: [], border: [] },
        });
        for (const imported of result.unresolvedImports) {
            table.push([
                imported.edge.targetName ?? '(unresolved)',
                formatResolution(imported.edge.status, c),
                imported.edge.specifier ?? imported.edge.targetName ?? '',
            ]);
        }
        sections.push(tableTitle('Unresolved Imports', addSeparators(table.toString(), ci), ci));
    }
    return appendInlineContexts(`${sections.join('\n\n')}\n`, [
        ...result.importers,
        ...result.localImports,
        ...result.externalImports,
        ...result.unresolvedImports,
    ]);
}
export function formatClassTree(result, ci = false) {
    const c = palette(ci);
    if (!result)
        return `${c.dim('Class not found.')}\n`;
    const typeLabel = result.target.type === 'interface' ? 'Interface' : result.target.type === 'enum' ? 'Enum' : 'Class';
    const lines = [];
    lines.push(c.header(`${typeLabel}: ${result.target.name} (${formatLocation(result.target)})`));
    if (result.parents.length > 0) {
        lines.push('');
        lines.push(c.key('Parents'));
        for (const parent of result.parents) {
            const relation = parent.edge.type === 'implements' ? 'implements' : 'extends';
            lines.push(`  ${parent.to?.name ?? parent.edge.targetName ?? '(unresolved)'} [${relation}, ${parent.edge.status}]`);
        }
    }
    if (result.children.length > 0) {
        lines.push('');
        lines.push(c.key('Children'));
        for (const child of result.children) {
            lines.push(`  ${child.from.name} (${formatLocation(child.from)})`);
        }
    }
    if (result.methods.length > 0) {
        lines.push('');
        lines.push(c.key(`Methods (${formatNumber(result.methods.length)})`));
        for (const method of result.methods) {
            lines.push(`  ${method.name}${method.signature ? `(${method.signature})` : ''}`);
        }
    }
    return `${lines.join('\n')}\n`;
}
export function formatChains(chains, ci = false) {
    const c = palette(ci);
    if (chains.length === 0)
        return `${c.dim('No call chain found.')}\n`;
    const lines = [];
    for (let index = 0; index < chains.length; index++) {
        const chain = chains[index];
        const title = chain.status === 'blocked_ambiguous'
            ? `Chain ${index + 1} (blocked by ambiguity)`
            : chain.direction === 'reverse'
                ? `Chain ${index + 1} (reverse path, depth ${chain.edges.length})`
                : `Chain ${index + 1} (depth ${chain.edges.length})`;
        lines.push(c.header(title));
        if (chain.direction === 'reverse') {
            lines.push(c.dim('Calls flow in the opposite direction of the requested query.'));
        }
        for (let step = 0; step < chain.nodes.length; step++) {
            const node = chain.nodes[step];
            lines.push(`${'  '.repeat(step)}${node.name} (${formatLocation(node)})`);
            const edge = chain.edges[step];
            if (edge) {
                const extra = edge.status === 'ambiguous' && edge.candidates && edge.candidates.length > 0
                    ? ` (${edge.candidates.length} candidates)`
                    : '';
                lines.push(`${'  '.repeat(step)}${c.dim(`calls [${edge.status}] at line ${edge.line ?? '?'}${extra}`)}`);
            }
        }
        if (chain.status === 'blocked_ambiguous' && chain.blockedBy && chain.blockedAt) {
            const candidates = chain.blockedBy.candidates?.slice(0, 3).map(formatCandidate).join(', ') ?? '';
            const extra = candidates ? `: ${candidates}` : '';
            lines.push(`${'  '.repeat(chain.nodes.length - 1)}${c.dim(`blocked by ambiguous call "${chain.blockedBy.targetName ?? '(unknown)'}" at line ${chain.blockedBy.line ?? '?'}${extra}`)}`);
        }
        if (index < chains.length - 1)
            lines.push('');
    }
    return lines.join('\n') + '\n';
}
export function formatFusedResults(results, ci = false) {
    const c = palette(ci);
    if (results.length === 0)
        return `${c.dim('No fused search results found.')}\n`;
    const withRank = results.some(result => result.pageRank !== undefined);
    const table = new Table({
        head: [
            c.key('Name'),
            c.key('Type'),
            c.key('Location'),
            c.key('Excerpt'),
            c.key('Score'),
            ...(withRank ? [c.key('Rank')] : []),
            c.key('Sources'),
        ],
        chars: tableChars(ci),
        style: { head: [], border: [] },
    });
    for (const result of results) {
        table.push([
            c.value(result.name.slice(0, 60)),
            result.type,
            result.relativePath ? `${result.relativePath}${result.line ? `:${result.line}` : ''}` : result.path,
            result.excerpt?.slice(0, 80) ?? '',
            result.score.toFixed(4),
            ...(withRank ? [result.pageRank !== undefined ? result.pageRank.toFixed(1) : ''] : []),
            result.sources.map(s => `${s.method}#${s.rank}`).join(', '),
        ]);
    }
    return appendInlineContexts(`${tableTitle('Fused Search', addSeparators(table.toString(), ci), ci)}\n`, results);
}
export function formatModuleCoupling(result, ci = false) {
    const c = palette(ci);
    const lines = [];
    lines.push(c.header(`Module: ${result.module}`));
    lines.push(`Public symbols: ${result.publicSymbolCount}`);
    lines.push(`Afferent coupling (Ca): ${result.afferentCoupling}`);
    lines.push(`Efferent coupling (Ce): ${result.efferentCoupling}`);
    lines.push(`Instability: ${result.instability}`);
    if (result.topImporters.length > 0) {
        lines.push('');
        lines.push(c.key('Top Importers'));
        for (const importer of result.topImporters)
            lines.push(`  ${importer}`);
    }
    if (result.topDependencies.length > 0) {
        lines.push('');
        lines.push(c.key('Top Dependencies'));
        for (const dependency of result.topDependencies)
            lines.push(`  ${dependency}`);
    }
    if (result.keyClasses.length > 0) {
        lines.push('');
        lines.push(c.key('Key Classes'));
        for (const item of result.keyClasses) {
            const parentPart = item.parents.length > 0 ? ` extends ${item.parents.join(', ')}` : '';
            lines.push(`  ${item.name}${parentPart} (${item.methodCount} methods, ${item.childCount} children)`);
        }
    }
    return lines.join('\n') + '\n';
}
export function formatSymbolHashes(results, ci = false) {
    const c = palette(ci);
    const counts = { resolved: 0, ambiguous: 0, not_found: 0, error: 0 };
    for (const result of results)
        counts[result.status]++;
    const lines = [
        c.header(`# Symbol Hashes — ${counts.resolved} resolved, ${counts.ambiguous} ambiguous, ${counts.not_found} not found, ${counts.error} error(s)`),
    ];
    for (const result of results) {
        const label = result.status === 'error' ? (result.query?.name ?? '(invalid query)') : result.query.name;
        if (result.status === 'resolved') {
            const { symbol } = result;
            const container = symbol.key.containerName ? ` in ${symbol.key.containerName}` : '';
            const span = symbol.spanExact ? 'exact span' : 'declaration-only span';
            lines.push(`${c.ok('resolved')}   ${label} (${symbol.key.kind}${container}) ${symbol.key.relativePath}:${symbol.line}-${symbol.endLine} [${span}]`);
            lines.push(`           sha256 ${symbol.bodyHash}`);
            if (symbol.exactSpanHash) {
                lines.push(`           exact-span sha256 ${symbol.exactSpanHash}`);
            }
            lines.push(`           verification v${symbol.hashes.hashVersion}/${symbol.hashes.algo} normalized=${symbol.hashes.normalizedHash ?? '-'} structural=${symbol.hashes.structuralHash ?? '-'}`);
            if (result.verdict)
                lines.push(`           verdict ${result.verdict}`);
            continue;
        }
        if (result.status === 'error') {
            lines.push(`${c.bad('error')}      ${label} — ${result.message}`);
            continue;
        }
        const color = result.status === 'ambiguous' ? c.warn : c.bad;
        lines.push(`${color(result.status)}  ${label} — ${result.message}`);
        if (result.status === 'not_found' && result.verdict) {
            lines.push(`           verdict ${result.verdict}`);
            if (result.lineage?.status === 'resolved') {
                const successor = result.lineage.successor;
                lines.push(`           successor ${successor.name} ${successor.relativePath ?? successor.path}:${successor.line ?? '?'}`);
            }
            else if (result.lineage?.status === 'ambiguous') {
                lines.push(`           lineage ambiguous (${result.lineage.candidates.length} candidates)`);
            }
            else if (result.lineage?.status === 'unavailable') {
                lines.push('           lineage unavailable');
            }
        }
        for (const candidate of result.candidates) {
            lines.push(`           - ${candidate.name} ${candidate.relativePath ?? candidate.path}:${candidate.line ?? '?'}`);
        }
    }
    return lines.join('\n') + '\n';
}
export function formatCodeDocs(results, ci = false) {
    const c = palette(ci);
    const lines = [];
    for (const result of results) {
        if (result.status === 'resolved') {
            const location = `${result.relativePath}:${result.line}-${result.endLine}`;
            lines.push(`${c.ok('resolved')}  ${result.name} ${result.signature} (${location})`);
            if (result.docComment)
                lines.push(...result.docComment.split('\n').map(line => `  ${line}`));
            lines.push(`  params: ${result.params.length > 0 ? result.params.join(' | ') : '(none)'}`);
            lines.push(`  body: ${result.bodyTokens} ${result.tokenizer} tokens (not included)`);
            continue;
        }
        if (result.status === 'error') {
            lines.push(`${c.bad('error')}  ${result.message}`);
            continue;
        }
        const color = result.status === 'ambiguous' ? c.warn : c.bad;
        lines.push(`${color(result.status)}  ${result.message}`);
        if ('candidates' in result) {
            for (const candidate of result.candidates) {
                lines.push(`  - ${candidate.name} ${candidate.relativePath ?? candidate.path}:${candidate.line ?? '?'}`);
            }
        }
    }
    return `${lines.join('\n')}\n`;
}
export function formatCodeSeams(seams, ci = false) {
    const c = palette(ci);
    if (seams.length === 0)
        return `${c.dim('No cross-module seams found.')}\n`;
    const table = new Table({
        head: [c.key('Rank'), c.key('Seam'), c.key('Imports'), c.key('Calls'), c.key('Co-change')],
        chars: tableChars(ci),
        style: { head: [], border: [] },
    });
    for (const seam of seams) {
        const cochange = seam.cochange
            ? `${seam.cochange.count}x lift ${seam.cochange.lift.toFixed(2)} last ${seam.cochange.lastCochangedAt.slice(0, 10)}`
            : '';
        table.push([
            String(seam.rank),
            `${seam.moduleA || '.'} <-> ${seam.moduleB || '.'}`,
            formatNumber(seam.importEdgeCount),
            formatNumber(seam.callEdgeCount),
            cochange,
        ]);
    }
    return appendInlineContexts(`${tableTitle(`Code Seams (${seams.length})`, addSeparators(table.toString(), ci), ci)}\n`, seams);
}
export function formatSeamResolutions(results, ci = false) {
    const c = palette(ci);
    const missing = results.filter(result => result.status === 'missing').length;
    const lines = [c.header(`# Seam Resolution — ${results.length - missing} resolved, ${missing} missing`)];
    for (const result of results) {
        if (result.status === 'resolved' && result.seam) {
            lines.push(`${c.ok('resolved')}  ${result.seamKey} (${result.seam.importEdgeCount} imports, ${result.seam.callEdgeCount} calls)`);
        }
        else {
            lines.push(`${c.bad('missing')}   ${result.seamKey}`);
        }
    }
    return appendInlineContexts(`${lines.join('\n')}\n`, results.flatMap(result => (result.seam ? [result.seam] : [])));
}
export function formatCodeModuleClusters(result) {
    const coverage = result.honesty.coverage;
    const lines = [
        `Module clusters: ${result.clusters.length}/${result.total} shown; coverage=${coverage.status}; ` +
            `unclustered=${result.unclusteredFiles}; truncated=${result.truncated}; omitted=${result.omitted}`,
    ];
    for (const cluster of result.clusters) {
        lines.push(`${cluster.id}  ${cluster.label}  size=${cluster.size} cohesion=${cluster.cohesion.toFixed(6)} ` +
            `entry-points=${cluster.entryPoints.length} dependencies=${cluster.dependencies.length}`);
        lines.push(`  members: ${cluster.members.join(', ')}`);
        if (cluster.keywords.length > 0)
            lines.push(`  keywords: ${cluster.keywords.join(', ')}`);
        for (const entry of cluster.entryPoints) {
            lines.push(`  entry: ${entry.name} (${entry.kind}) ${entry.relativePath}:${entry.span.startLine}`);
        }
        for (const dependency of cluster.dependencies) {
            lines.push(`  depends: ${dependency.toClusterId} imports=${dependency.importCount}`);
        }
        if (cluster.truncated) {
            lines.push(`  omitted: members=${cluster.omitted.members} entry-points=${cluster.omitted.entryPoints} ` +
                `dependencies=${cluster.omitted.dependencies}`);
        }
    }
    if (result.clusters.length === 0)
        lines.push('No multi-file module clusters found.');
    return `${lines.join('\n')}\n`;
}
export function formatCodeFlowTraces(result) {
    const coverage = result.honesty.coverage;
    const lines = [
        `Code flows: ${result.traces.length}/${result.total} shown; coverage=${coverage.status}; ` +
            `truncated=${result.truncated}; omitted=${result.omitted}`,
    ];
    for (const trace of result.traces) {
        lines.push(`${trace.id}  ${trace.label}  steps=${trace.steps.length} cross-cluster=${trace.crossCluster} ` +
            `score=${trace.entryPoint.score.toFixed(2)}`);
        for (const step of trace.steps) {
            lines.push(`  ${step.depth}: ${step.name} (${step.kind}) ${step.relativePath}:${step.span.startLine}` +
                `${step.clusterId ? ` [${step.clusterId}]` : ''}`);
        }
        if (trace.truncated) {
            lines.push(`  omitted: branching=${trace.omitted.branchingTargets} depth=${trace.omitted.depthFrontierTargets} ` +
                `steps=${trace.omitted.stepLimitTargets}`);
        }
    }
    if (result.traces.length === 0)
        lines.push('No qualifying multi-step flows found.');
    return `${lines.join('\n')}\n`;
}
// ── Repo map / pack ───────────────────────────────────────────────────────
// Repo-map output is meant to be pasted into an LLM, so it is deliberately
// plain (no ANSI colors) regardless of the `ci` flag.
export function formatRepoMapMarkdown(result) {
    return renderRepoMapMarkdown(result);
}
export function formatRepoMapPlain(result) {
    return renderRepoMapPlain(result);
}
export function formatRepoMapXml(result) {
    return renderRepoMapXml(result);
}
export function renderRepoMap(result, pretty = false) {
    return renderBudgetedRepoMap(result, pretty);
}
//# sourceMappingURL=output.js.map