import Table from 'cli-table3';
import chalk from 'chalk';
import { formatNumber } from '../utils.js';
import { sectionHeader, stripAnsi, tableChars } from '../output/tabular.js';
import { repoMapEntryToMarkdown, repoMapSignatureLine } from './map.js';
import { codeDiffSymbolLine } from './diff.js';
import { directoryTreeFooter, directoryTreeHeader } from './tree.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 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.status === 'ambiguous' && 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 function formatPayloadJson(payload) {
    return JSON.stringify(payload, null, 2);
}
export function formatCodeDiffJson(result) {
    return JSON.stringify(result, null, 2);
}
export function formatDirectoryTreeJson(result) {
    return JSON.stringify(result, null, 2);
}
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) {
    const c = palette(ci);
    const lines = [
        c.header(`# Changed symbols (${result.range.describe})`),
        `${result.summary.filesChanged} files · ${result.summary.symbolsChanged} symbols · +${result.summary.totalChurn.added}/-${result.summary.totalChurn.deleted} · ${result.summary.newExports} new exports · ${result.summary.signatureChanges} signature changes`,
    ];
    if (result.truncated) {
        lines.push(c.warn(`truncated: dropped ${result.summary.droppedSymbols} symbol(s)`));
    }
    let currentFile = '';
    for (const symbol of result.symbols) {
        if (symbol.relativePath !== currentFile) {
            currentFile = symbol.relativePath;
            lines.push('', c.key(`## ${currentFile}`));
        }
        lines.push(`  ${codeDiffSymbolLine(symbol)}`);
    }
    if (result.symbols.length === 0)
        lines.push('', c.dim('No changed symbols admitted within the token budget.'));
    return `${lines.join('\n')}\n`;
}
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 `${tableTitle(title, addSeparators(table.toString(), ci), ci)}\n`;
}
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 `${tableTitle(title, addSeparators(table.toString(), ci), ci)}\n`;
}
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 sections.join('\n\n') + '\n';
}
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 table = new Table({
        head: [c.key('Name'), c.key('Type'), c.key('Location'), c.key('Excerpt'), c.key('Score'), 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),
            result.sources.map(s => `${s.method}#${s.rank}`).join(', '),
        ]);
    }
    return `${tableTitle('Fused Search', addSeparators(table.toString(), ci), ci)}\n`;
}
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';
}
// ── 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.
/** One-line description of an active focus, or '' when there is none. */
function focusDescriptor(result) {
    const focus = result.focus;
    if (!focus)
        return '';
    const parts = [];
    if (focus.query)
        parts.push(`query "${focus.query}"`);
    if (focus.paths.length > 0)
        parts.push(`${focus.paths.length} path(s)`);
    const what = parts.length > 0 ? parts.join(' + ') : 'focus';
    return `${what}, ${focus.matchedFiles} matched`;
}
function repoMapHeader(result) {
    const tokenizer = result.tokenizer && result.tokenizer !== 'heuristic' ? `, ${result.tokenizer}` : '';
    const focus = result.focus ? ` — focus: ${focusDescriptor(result)}` : '';
    return `# Repo Map — ${result.entries.length}/${result.totalFiles} files, ${result.tokensUsed}/${result.tokenBudget} tokens${tokenizer}${focus}`;
}
function repoMapFooter(result) {
    if (!result.truncated)
        return null;
    const parts = [];
    if (result.droppedFiles > 0)
        parts.push(`${result.droppedFiles} file(s)`);
    if (result.droppedSymbols > 0)
        parts.push(`${result.droppedSymbols} symbol(s)`);
    return `_${parts.join(' / ')} omitted to fit the ${result.tokenBudget}-token budget._`;
}
export function formatRepoMapMarkdown(result) {
    const blocks = [repoMapHeader(result), ''];
    for (const entry of result.entries)
        blocks.push(repoMapEntryToMarkdown(entry, result.mode));
    const footer = repoMapFooter(result);
    if (footer)
        blocks.push('', footer);
    return blocks.join('\n\n') + '\n';
}
export function formatRepoMapPlain(result) {
    const focus = result.focus ? ` — focus: ${focusDescriptor(result)}` : '';
    const lines = [
        `Repo Map (${result.entries.length}/${result.totalFiles} files, ${result.tokensUsed}/${result.tokenBudget} tokens)${focus}`,
        '',
    ];
    for (const entry of result.entries) {
        lines.push(`${entry.relativePath}  [${entry.normalizedScore.toFixed(1)}]`);
        if (result.mode === 'pack' && entry.content != null) {
            for (const line of entry.content.split('\n'))
                lines.push(`    ${line}`);
        }
        else {
            for (const symbol of entry.symbols) {
                lines.push(`    ${repoMapSignatureLine(symbol)}`);
            }
        }
        lines.push('');
    }
    const footer = repoMapFooter(result);
    if (footer)
        lines.push(footer.replace(/_/g, ''));
    return lines.join('\n') + '\n';
}
function escapeXml(value) {
    return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function repoMapEntryToXml(entry, mode) {
    const focusAttr = entry.focusScore != null ? ` focusScore="${entry.focusScore.toFixed(1)}"` : '';
    const lines = [
        `  <file path="${escapeXml(entry.relativePath)}" score="${entry.normalizedScore.toFixed(1)}"${focusAttr} language="${escapeXml(entry.language)}">`,
    ];
    for (const symbol of entry.symbols) {
        lines.push(`    <symbol kind="${escapeXml(symbol.kind)}" exported="${symbol.exported}"${symbol.line != null ? ` line="${symbol.line}"` : ''}>${escapeXml(symbol.signature)}</symbol>`);
    }
    if (mode === 'pack' && entry.content != null) {
        lines.push(`    <content${entry.contentReason ? ` reason="${escapeXml(entry.contentReason)}"` : ''}>${escapeXml(entry.content)}</content>`);
    }
    lines.push('  </file>');
    return lines.join('\n');
}
function repoMapFocusXmlAttrs(result) {
    const focus = result.focus;
    if (!focus)
        return '';
    const query = focus.query ? ` focusQuery="${escapeXml(focus.query)}"` : '';
    const paths = focus.paths.length > 0 ? ` focusPaths="${escapeXml(focus.paths.join(';'))}"` : '';
    return `${query}${paths} focusMatched="${focus.matchedFiles}"`;
}
export function formatRepoMapXml(result) {
    const lines = [
        `<repomap mode="${result.mode}" tokens="${result.tokensUsed}" budget="${result.tokenBudget}" files="${result.entries.length}" totalFiles="${result.totalFiles}"${result.truncated ? ' truncated="true"' : ''}${repoMapFocusXmlAttrs(result)}>`,
    ];
    for (const entry of result.entries)
        lines.push(repoMapEntryToXml(entry, result.mode));
    lines.push('</repomap>');
    return lines.join('\n') + '\n';
}
export function renderRepoMap(result) {
    switch (result.format) {
        case 'json':
            return JSON.stringify(result, null, 2) + '\n';
        case 'xml':
            return formatRepoMapXml(result);
        case 'plain':
            return formatRepoMapPlain(result);
        case 'markdown':
        default:
            return formatRepoMapMarkdown(result);
    }
}
//# sourceMappingURL=output.js.map