import { writeFile } from 'node:fs/promises';
import path from 'node:path';
import { z } from 'zod';
import { analyzeWorkspace } from './analyze.js';
import { describeWorkspace } from './describe.js';
import { formatWorkspaceDescription } from './describe-output.js';
import { inspectWorkspaceDocumentSet } from './documents.js';
import { DEFAULT_EXCLUDE_DIRS, writeStream } from '../utils.js';
const WorkspaceCommandOptionsSchema = z
    .object({
    format: z.string().optional(),
    output: z.string().optional(),
    excludeDir: z.string().optional(),
    ignorePattern: z.array(z.string()).optional(),
    largeFileLimit: z.string().optional(),
    gitignore: z.boolean().optional(),
    code: z.boolean().optional(),
    documents: z.boolean().optional(),
    structures: z.boolean().optional(),
    maxFiles: z.string().optional(),
    maxReferenceFiles: z.string().optional(),
    maxProjects: z.string().optional(),
    includeMarkdown: z.boolean().optional(),
    quiet: z.boolean().optional(),
    full: z.boolean().optional(),
    ci: z.boolean().optional(),
})
    .passthrough();
function collectOption(value, previous = []) {
    return [...previous, value];
}
function getOptions(command) {
    return WorkspaceCommandOptionsSchema.parse(command.optsWithGlobals());
}
async function emit(output, options) {
    if (options.output) {
        await writeFile(options.output, output);
        return;
    }
    await writeStream(process.stdout, output);
}
async function runWorkspaceAction(action) {
    try {
        await action();
    }
    catch (err) {
        const error = err;
        await writeStream(process.stderr, `Error: ${error.message}\n`);
        process.exit(1);
    }
}
/**
 * Normalize output to ASCII-only, color-free text for CI logs: strip ANSI
 * escape sequences, fold common typographic punctuation to ASCII, and drop any
 * remaining non-ASCII characters.
 */
export function toCiSafe(text) {
    return text
        .replace(/\[[0-9;]*m/g, '')
        .replace(/[‐-―]/g, '-')
        .replace(/[‘’‛]/g, "'")
        .replace(/[“”]/g, '"')
        .replace(/…/g, '...')
        .replace(/[•·]/g, '*')
        .normalize('NFKD')
        .replace(/[̀-ͯ]/g, '')
        .replace(/[^\x00-\x7f]/g, '');
}
function parsePositiveInt(value, fallback) {
    const parsed = Number.parseInt(value ?? '', 10);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}
function parseNonNegativeInt(value, fallback) {
    const parsed = Number.parseInt(value ?? '', 10);
    return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
}
async function describeDirectories(directories, options) {
    const roots = directories.length > 0 ? directories : [process.cwd()];
    const results = [];
    for (const root of roots) {
        results.push(await describeWorkspace(path.resolve(root), {
            excludeDir: options.excludeDir
                ?.split(',')
                .map(item => item.trim())
                .filter(Boolean),
            ignorePatterns: options.ignorePattern,
            noGitignore: options.gitignore === false,
            maxProjects: parseNonNegativeInt(options.maxProjects, 25),
        }));
    }
    if (options.format === 'json') {
        return JSON.stringify(results.length === 1 ? results[0] : results, null, 2);
    }
    const outputs = results.map(result => formatWorkspaceDescription(result, {
        format: 'tabular',
        quiet: options.quiet === true,
        full: options.full === true,
    }));
    const rendered = outputs.join(options.quiet ? '\n' : '\n\n');
    return options.ci === true ? toCiSafe(rendered) : rendered;
}
function formatWorkspaceAnalysis(result) {
    const lines = [];
    lines.push(`Workspace: ${result.rootDir}`);
    lines.push(`Elapsed: ${result.summary.elapsedMs}ms`);
    if (result.code) {
        lines.push(`Code: ${result.summary.code?.totalFiles ?? 0} files, ${result.summary.code?.totalLines ?? 0} lines, ${result.code.languages.length} language rows`);
    }
    if (result.documents) {
        lines.push(`Documents: ${result.documents.aggregates.totals.files} files, ${result.documents.aggregates.totals.words} words`);
    }
    lines.push(`Structures: ${result.structures.length}`);
    if (result.skipped.length > 0)
        lines.push(`Skipped: ${result.skipped.length}`);
    if (result.errors.length > 0)
        lines.push(`Errors: ${result.errors.length}`);
    return lines.join('\n');
}
function formatWorkspaceDocumentSet(result) {
    const lines = [];
    lines.push(`Workspace: ${result.rootDir}`);
    lines.push(`Documents: ${result.documents.length}`);
    lines.push(`Cross references: ${result.crossReferences.length}`);
    lines.push(`Unresolved mentions: ${result.unresolvedMentions.length}`);
    if (result.skipped.length > 0)
        lines.push(`Skipped: ${result.skipped.length}`);
    if (result.errors.length > 0)
        lines.push(`Errors: ${result.errors.length}`);
    return lines.join('\n');
}
export function registerWorkspaceCommands(program) {
    const workspace = program.command('workspace').description('Workspace-level analysis and document context helpers');
    const describe = workspace
        .command('describe [rootDir]')
        .description('Quickly classify a workspace from fast file and manifest signals');
    describe
        .option('--format <type>', 'output format: tabular or json', 'tabular')
        .option('-o, --output <file>', 'write output to file')
        .option('--exclude-dir <dirs>', 'directories to skip (comma-separated)', DEFAULT_EXCLUDE_DIRS.join(','))
        .option('--ignore-pattern <pattern>', 'gitignore-style pattern to ignore', collectOption, [])
        .option('--no-gitignore', 'disable .gitignore respect')
        .option('--max-projects <n>', 'maximum nested project summaries to show', '25')
        .option('--quiet', 'show only the classification summary')
        .option('--full', 'show the full description report')
        .option('--ci', 'ASCII-only output, no colors');
    describe.action(async (rootDir, _opts, command) => {
        await runWorkspaceAction(async () => {
            const options = getOptions(command);
            const output = await describeDirectories(rootDir ? [rootDir] : [], options);
            await emit(output.endsWith('\n') ? output : `${output}\n`, options);
        });
    });
    const analyze = workspace
        .command('analyze [rootDir]')
        .description('Analyze workspace code, documents, and structures with a versioned JSON contract');
    analyze
        .option('--format <type>', 'output format: tabular or json', 'json')
        .option('-o, --output <file>', 'write output to file')
        .option('--exclude-dir <dirs>', 'directories to skip (comma-separated)', DEFAULT_EXCLUDE_DIRS.join(','))
        .option('--ignore-pattern <pattern>', 'gitignore-style pattern to ignore', collectOption, [])
        .option('--large-file-limit <mb>', 'skip files over this size in MB', '50')
        .option('--no-gitignore', 'disable .gitignore respect')
        .option('--no-code', 'skip code analysis')
        .option('--no-documents', 'skip document analysis')
        .option('--no-structures', 'skip structure extraction');
    analyze.action(async (rootDir, _opts, command) => {
        await runWorkspaceAction(async () => {
            const options = getOptions(command);
            const result = await analyzeWorkspace(path.resolve(rootDir ?? process.cwd()), {
                excludeDir: options.excludeDir
                    ?.split(',')
                    .map(item => item.trim())
                    .filter(Boolean),
                ignorePatterns: options.ignorePattern,
                noGitignore: options.gitignore === false,
                largeFileLimitMb: parsePositiveInt(options.largeFileLimit, 50),
                includeCode: options.code !== false,
                includeDocuments: options.documents !== false,
                includeStructures: options.structures !== false,
            });
            const output = options.format === 'tabular' ? formatWorkspaceAnalysis(result) : JSON.stringify(result, null, 2);
            await emit(output.endsWith('\n') ? output : `${output}\n`, options);
        });
    });
    const documents = workspace
        .command('documents [rootDir]')
        .description('Inspect workspace document summaries and cross-document references');
    documents
        .option('--format <type>', 'output format: tabular or json', 'json')
        .option('-o, --output <file>', 'write output to file')
        .option('--exclude-dir <dirs>', 'directories to skip (comma-separated)', DEFAULT_EXCLUDE_DIRS.join(','))
        .option('--ignore-pattern <pattern>', 'gitignore-style pattern to ignore', collectOption, [])
        .option('--large-file-limit <mb>', 'skip files over this size in MB', '50')
        .option('--no-gitignore', 'disable .gitignore respect')
        .option('--max-files <n>', 'maximum documents to inspect', '50')
        .option('--max-reference-files <n>', 'maximum documents to convert for cross-reference detection', '20')
        .option('--include-markdown', 'include markdown content in per-document summaries');
    documents.action(async (rootDir, _opts, command) => {
        await runWorkspaceAction(async () => {
            const options = getOptions(command);
            const result = await inspectWorkspaceDocumentSet(path.resolve(rootDir ?? process.cwd()), {
                excludeDir: options.excludeDir
                    ?.split(',')
                    .map(item => item.trim())
                    .filter(Boolean),
                ignorePatterns: options.ignorePattern,
                noGitignore: options.gitignore === false,
                largeFileLimitMb: parsePositiveInt(options.largeFileLimit, 50),
                maxFiles: parsePositiveInt(options.maxFiles, 50),
                maxReferenceFiles: parsePositiveInt(options.maxReferenceFiles, 20),
                includeMarkdown: options.includeMarkdown === true,
            });
            const output = options.format === 'tabular' ? formatWorkspaceDocumentSet(result) : JSON.stringify(result, null, 2);
            await emit(output.endsWith('\n') ? output : `${output}\n`, options);
        });
    });
}
export function registerDescribeCommand(program) {
    const describe = program
        .command('describe [directories...]')
        .description('Quickly classify one or more directories from fast file and manifest signals');
    describe
        .option('--format <type>', 'output format: tabular or json', 'tabular')
        .option('-o, --output <file>', 'write output to file')
        .option('--exclude-dir <dirs>', 'directories to skip (comma-separated)', DEFAULT_EXCLUDE_DIRS.join(','))
        .option('--ignore-pattern <pattern>', 'gitignore-style pattern to ignore', collectOption, [])
        .option('--no-gitignore', 'disable .gitignore respect')
        .option('--max-projects <n>', 'maximum nested project summaries to show', '25')
        .option('--quiet', 'show only the classification summary')
        .option('--full', 'show the full description report')
        .option('--ci', 'ASCII-only output, no colors');
    describe.action(async (directories, _opts, command) => {
        await runWorkspaceAction(async () => {
            const options = getOptions(command);
            const output = await describeDirectories(directories ?? [], options);
            await emit(output.endsWith('\n') ? output : `${output}\n`, options);
        });
    });
}
//# sourceMappingURL=command.js.map