/**
 * Wiki Command
 *
 * Generates repository documentation from the knowledge graph.
 * Usage: codragraph wiki [path] [options]
 */
import path from 'path';
import readline from 'readline';
import { execSync, execFileSync } from 'child_process';
import cliProgress from 'cli-progress';
import { getGitRoot, isGitRepo } from '../storage/git.js';
import { getStoragePaths, loadMeta, loadCLIConfig, saveCLIConfig, } from '../storage/repo-manager.js';
import { WikiGenerator } from '../core/wiki/generator.js';
import { resolveLLMConfig } from '../core/wiki/llm-client.js';
import { detectCursorCLI } from '../core/wiki/cursor-client.js';
/**
 * Prompt the user for input via stdin.
 */
function prompt(question, hide = false) {
    return new Promise((resolve) => {
        const rl = readline.createInterface({
            input: process.stdin,
            output: process.stdout,
        });
        if (hide && process.stdin.isTTY) {
            // Mask input for API keys
            process.stdout.write(question);
            let input = '';
            process.stdin.setRawMode(true);
            process.stdin.resume();
            process.stdin.setEncoding('utf-8');
            const onData = (char) => {
                if (char === '\n' || char === '\r' || char === '\u0004') {
                    process.stdin.setRawMode(false);
                    process.stdin.removeListener('data', onData);
                    process.stdout.write('\n');
                    rl.close();
                    resolve(input);
                }
                else if (char === '\u0003') {
                    // Ctrl+C
                    process.stdin.setRawMode(false);
                    rl.close();
                    process.exit(1);
                }
                else if (char === '\u007F' || char === '\b') {
                    // Backspace
                    if (input.length > 0) {
                        input = input.slice(0, -1);
                        process.stdout.write('\b \b');
                    }
                }
                else {
                    input += char;
                    process.stdout.write('*');
                }
            };
            process.stdin.on('data', onData);
        }
        else {
            rl.question(question, (answer) => {
                rl.close();
                resolve(answer.trim());
            });
        }
    });
}
export const wikiCommand = async (inputPath, options) => {
    // Set verbose mode globally for cursor-client to pick up
    if (options?.verbose) {
        process.env.CODRAGRAPH_VERBOSE = '1';
    }
    console.log('\n  CodraGraph Wiki Generator\n');
    // ── Resolve repo path ───────────────────────────────────────────────
    let repoPath;
    if (inputPath) {
        repoPath = path.resolve(inputPath);
    }
    else {
        const gitRoot = getGitRoot(process.cwd());
        if (!gitRoot) {
            console.log('  Error: Not inside a git repository\n');
            process.exitCode = 1;
            return;
        }
        repoPath = gitRoot;
    }
    if (!isGitRepo(repoPath)) {
        console.log('  Error: Not a git repository\n');
        process.exitCode = 1;
        return;
    }
    // ── Check for existing index ────────────────────────────────────────
    const { storagePath, cgdbPath } = getStoragePaths(repoPath);
    const meta = await loadMeta(storagePath);
    if (!meta) {
        console.log('  Error: No CodraGraph index found.');
        console.log('  Run `codragraph analyze` first to index this repository.\n');
        process.exitCode = 1;
        return;
    }
    // ── Resolve LLM config (with interactive fallback) ─────────────────
    // Save any CLI overrides immediately
    if (options?.apiKey ||
        options?.model ||
        options?.baseUrl ||
        options?.provider ||
        options?.apiVersion ||
        options?.reasoningModel !== undefined) {
        const existing = await loadCLIConfig();
        const updates = {};
        if (options.apiKey)
            updates.apiKey = options.apiKey;
        if (options.baseUrl)
            updates.baseUrl = options.baseUrl;
        if (options.provider)
            updates.provider = options.provider;
        if (options.apiVersion)
            updates.apiVersion = options.apiVersion;
        if (options.reasoningModel !== undefined)
            updates.isReasoningModel = options.reasoningModel;
        // Save model to appropriate field based on provider
        if (options.model) {
            if (options.provider === 'cursor') {
                updates.cursorModel = options.model;
            }
            else {
                updates.model = options.model;
            }
        }
        await saveCLIConfig({ ...existing, ...updates });
        console.log('  Config saved to ~/.codragraph/config.json\n');
    }
    const savedConfig = await loadCLIConfig();
    const hasSavedConfig = !!(savedConfig.provider === 'cursor' ||
        (savedConfig.apiKey && savedConfig.baseUrl));
    const hasCLIOverrides = !!(options?.apiKey ||
        options?.model ||
        options?.baseUrl ||
        options?.provider ||
        options?.apiVersion ||
        options?.reasoningModel !== undefined);
    let llmConfig = await resolveLLMConfig({
        model: options?.model,
        baseUrl: options?.baseUrl,
        apiKey: options?.apiKey,
        provider: options?.provider,
        apiVersion: options?.apiVersion,
        isReasoningModel: options?.reasoningModel,
    });
    // Run interactive setup if no saved config and no CLI flags provided
    // (even if env vars exist — let user explicitly choose their provider)
    if (!hasSavedConfig && !hasCLIOverrides) {
        if (!process.stdin.isTTY) {
            // Non-interactive mode — need either API key or Cursor CLI
            if (!llmConfig.apiKey && llmConfig.provider !== 'cursor') {
                console.log('  Error: No LLM API key found.');
                console.log('  Set OPENAI_API_KEY or CODRAGRAPH_API_KEY environment variable,');
                console.log('  or pass --api-key <key>, or use --provider cursor.\n');
                process.exitCode = 1;
                return;
            }
            // Non-interactive with env var or cursor — just use it
        }
        else {
            console.log("  No LLM configured. Let's set it up.\n");
            console.log('  Supports OpenAI, OpenRouter, Azure, any OpenAI-compatible API, or Cursor CLI.\n');
            // Check if Cursor CLI is available
            const hasCursor = detectCursorCLI();
            // Provider selection
            console.log('  [1] OpenAI (api.openai.com)');
            console.log('  [2] OpenRouter (openrouter.ai)');
            console.log('  [3] Azure OpenAI');
            console.log('  [4] Custom endpoint');
            if (hasCursor) {
                console.log('  [5] Cursor CLI (local, uses your Cursor subscription)');
            }
            console.log('');
            const maxChoice = hasCursor ? '5' : '4';
            const choice = await prompt(`  Select provider (1/${maxChoice}): `);
            let baseUrl;
            let defaultModel;
            let provider = 'openai';
            let key = '';
            if (choice === '5' && hasCursor) {
                // Cursor CLI selected - model defaults to 'auto' (Cursor's default)
                provider = 'cursor';
                baseUrl = '';
                const modelInput = await prompt('  Model (leave empty for auto): ');
                const model = modelInput || '';
                // Save config for Cursor
                const cursorConfig = { provider: 'cursor' };
                if (model)
                    cursorConfig.cursorModel = model;
                await saveCLIConfig(cursorConfig);
                console.log('  Config saved to ~/.codragraph/config.json\n');
                llmConfig = { ...llmConfig, provider: 'cursor', model, apiKey: '', baseUrl: '' };
            }
            else if (choice === '3') {
                // Azure OpenAI guided setup — minimal prompts
                console.log('\n  Azure OpenAI setup.\n');
                const endpoint = (await prompt('  Endpoint URL (e.g. https://my-resource.openai.azure.com): '))
                    .trim()
                    .replace(/\/+$/, '');
                if (!endpoint) {
                    console.log('\n  No endpoint provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                const deploymentName = (await prompt('  Deployment name: ')).trim();
                if (!deploymentName) {
                    console.log('\n  No deployment name provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // API key — use env var if available
                const envKey = process.env.CODRAGRAPH_API_KEY || process.env.OPENAI_API_KEY || '';
                let azureKey;
                if (envKey) {
                    const masked = envKey.slice(0, 6) + '...' + envKey.slice(-4);
                    const useEnv = await prompt(`  Use existing env key (${masked})? (Y/n): `);
                    if (!useEnv || useEnv.toLowerCase() === 'y' || useEnv.toLowerCase() === 'yes') {
                        azureKey = envKey;
                    }
                    else {
                        azureKey = await prompt('  API key: ', true);
                    }
                }
                else {
                    azureKey = await prompt('  API key: ', true);
                }
                if (!azureKey) {
                    console.log('\n  No key provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // Always use v1 API format — no need for api-version
                const azureBaseUrl = `${endpoint}/openai/v1`;
                await saveCLIConfig({
                    apiKey: azureKey,
                    baseUrl: azureBaseUrl,
                    model: deploymentName,
                    provider: 'azure',
                });
                console.log('  Config saved to ~/.codragraph/config.json\n');
                llmConfig = {
                    ...llmConfig,
                    apiKey: azureKey,
                    baseUrl: azureBaseUrl,
                    model: deploymentName,
                    provider: 'azure',
                };
            }
            else {
                // OpenAI-compatible provider (OpenAI, OpenRouter, Custom)
                if (choice === '2') {
                    baseUrl = 'https://openrouter.ai/api/v1';
                    defaultModel = 'minimax/minimax-m2.5';
                    provider = 'openrouter';
                }
                else if (choice === '4') {
                    baseUrl = await prompt('  Base URL (e.g. http://localhost:11434/v1): ');
                    if (!baseUrl) {
                        console.log('\n  No URL provided. Aborting.\n');
                        process.exitCode = 1;
                        return;
                    }
                    defaultModel = 'gpt-4o-mini';
                    provider = 'custom';
                }
                else {
                    baseUrl = 'https://api.openai.com/v1';
                    defaultModel = 'gpt-4o-mini';
                    provider = 'openai';
                }
                // Model
                const modelInput = await prompt(`  Model (default: ${defaultModel}): `);
                const model = modelInput || defaultModel;
                // API key — pre-fill hint if env var exists
                const envKey = process.env.CODRAGRAPH_API_KEY || process.env.OPENAI_API_KEY || '';
                if (envKey) {
                    const masked = envKey.slice(0, 6) + '...' + envKey.slice(-4);
                    const useEnv = await prompt(`  Use existing env key (${masked})? (Y/n): `);
                    if (!useEnv || useEnv.toLowerCase() === 'y' || useEnv.toLowerCase() === 'yes') {
                        key = envKey;
                    }
                    else {
                        key = await prompt('  API key: ', true);
                    }
                }
                else {
                    key = await prompt('  API key: ', true);
                }
                if (!key) {
                    console.log('\n  No key provided. Aborting.\n');
                    process.exitCode = 1;
                    return;
                }
                // Save
                await saveCLIConfig({ apiKey: key, baseUrl, model, provider });
                console.log('  Config saved to ~/.codragraph/config.json\n');
                llmConfig = { ...llmConfig, apiKey: key, baseUrl, model, provider };
            }
        }
    }
    // ── Setup progress bar with elapsed timer ──────────────────────────
    const bar = new cliProgress.SingleBar({
        format: '  {bar} {percentage}% | {phase}',
        barCompleteChar: '\u2588',
        barIncompleteChar: '\u2591',
        hideCursor: true,
        barGlue: '',
        autopadding: true,
        clearOnComplete: false,
        stopOnComplete: false,
    }, cliProgress.Presets.shades_grey);
    bar.start(100, 0, { phase: 'Initializing...' });
    const t0 = Date.now();
    let lastPhase = '';
    let phaseStart = t0;
    // Tick elapsed time every second while stuck on the same phase
    const elapsedTimer = setInterval(() => {
        if (lastPhase) {
            const elapsed = Math.round((Date.now() - phaseStart) / 1000);
            if (elapsed >= 3) {
                bar.update({ phase: `${lastPhase} (${elapsed}s)` });
            }
        }
    }, 1000);
    // ── Run generator ───────────────────────────────────────────────────
    const wikiOptions = {
        force: options?.force,
        concurrency: options?.concurrency ? parseInt(options.concurrency, 10) : undefined,
        reviewOnly: options?.review,
    };
    const generator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, wikiOptions, (phase, percent, detail) => {
        const label = detail || phase;
        if (label !== lastPhase) {
            lastPhase = label;
            phaseStart = Date.now();
        }
        bar.update(percent, { phase: label });
    });
    try {
        const result = await generator.run();
        clearInterval(elapsedTimer);
        bar.stop();
        const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
        const wikiDir = path.join(storagePath, 'wiki');
        const viewerPath = path.join(wikiDir, 'index.html');
        const treeFile = path.join(wikiDir, 'module_tree.json');
        // Review mode: show module tree and ask for confirmation
        if (options?.review && result.moduleTree) {
            console.log(`\n  Module structure ready for review (${elapsed}s)\n`);
            console.log('  Modules to generate:\n');
            const printTree = (nodes, indent = 0) => {
                for (const node of nodes) {
                    const prefix = '  '.repeat(indent + 2);
                    const fileCount = node.files?.length || 0;
                    const childCount = node.children?.length || 0;
                    const suffix = fileCount > 0
                        ? ` (${fileCount} files)`
                        : childCount > 0
                            ? ` (${childCount} children)`
                            : '';
                    console.log(`${prefix}- ${node.name}${suffix}`);
                    if (node.children && node.children.length > 0) {
                        printTree(node.children, indent + 1);
                    }
                }
            };
            printTree(result.moduleTree);
            console.log(`\n  Tree saved to: ${treeFile}`);
            console.log('  You can edit this file to remove/rename modules.\n');
            // Ask for confirmation (auto-continue in non-interactive environments)
            if (!process.stdin.isTTY) {
                console.log('  Non-interactive mode — auto-continuing with generation.\n');
            }
            const answer = process.stdin.isTTY
                ? await prompt('  Continue with generation? (Y/n/edit): ')
                : 'y';
            const choice = answer.trim().toLowerCase();
            if (choice === 'n' || choice === 'no') {
                console.log('\n  Generation cancelled. Run `codragraph wiki` later to generate.\n');
                return;
            }
            if (choice === 'edit' || choice === 'e') {
                // Open editor for the user
                const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
                console.log(`\n  Opening ${treeFile} in ${editor}...`);
                console.log('  Save and close the editor when done.\n');
                try {
                    execFileSync(editor, [treeFile], { stdio: 'inherit' });
                }
                catch {
                    console.log(`  Could not open editor. Please edit manually:\n  ${treeFile}\n`);
                    console.log('  Then run `codragraph wiki` to continue.\n');
                    return;
                }
            }
            // Continue with generation using the (possibly edited) tree
            console.log('\n  Continuing with wiki generation...\n');
            bar.start(100, 30, { phase: 'Generating pages...' });
            // Re-run generator without reviewOnly flag
            const continueOptions = {
                ...wikiOptions,
                reviewOnly: false,
            };
            const continueGenerator = new WikiGenerator(repoPath, storagePath, cgdbPath, llmConfig, continueOptions, (phase, percent, detail) => {
                const label = detail || phase;
                if (label !== lastPhase) {
                    lastPhase = label;
                    phaseStart = Date.now();
                }
                bar.update(percent, { phase: label });
            });
            const continueResult = await continueGenerator.run();
            bar.update(100, { phase: 'Done' });
            bar.stop();
            const totalElapsed = ((Date.now() - t0) / 1000).toFixed(1);
            console.log(`\n  Wiki generated successfully (${totalElapsed}s)\n`);
            console.log(`  Mode: ${continueResult.mode}`);
            console.log(`  Pages: ${continueResult.pagesGenerated}`);
            console.log(`  Output: ${wikiDir}`);
            console.log(`  Viewer: ${viewerPath}`);
            if (continueResult.failedModules && continueResult.failedModules.length > 0) {
                console.log(`\n  Failed modules (${continueResult.failedModules.length}):`);
                for (const mod of continueResult.failedModules) {
                    console.log(`    - ${mod}`);
                }
            }
            console.log('');
            await maybePublishGist(viewerPath, options?.gist);
            return;
        }
        bar.update(100, { phase: 'Done' });
        if (result.mode === 'up-to-date' && !options?.force) {
            console.log('\n  Wiki is already up to date.');
            console.log(`  Viewer: ${viewerPath}\n`);
            await maybePublishGist(viewerPath, options?.gist);
            return;
        }
        console.log(`\n  Wiki generated successfully (${elapsed}s)\n`);
        console.log(`  Mode: ${result.mode}`);
        console.log(`  Pages: ${result.pagesGenerated}`);
        console.log(`  Output: ${wikiDir}`);
        console.log(`  Viewer: ${viewerPath}`);
        if (result.failedModules && result.failedModules.length > 0) {
            console.log(`\n  Failed modules (${result.failedModules.length}):`);
            for (const mod of result.failedModules) {
                console.log(`    - ${mod}`);
            }
            console.log('  Re-run to retry failed modules (pages will be regenerated).');
        }
        console.log('');
        await maybePublishGist(viewerPath, options?.gist);
    }
    catch (err) {
        clearInterval(elapsedTimer);
        bar.stop();
        if (err.message?.includes('No source files')) {
            console.log(`\n  ${err.message}\n`);
        }
        else if (err.message?.includes('content filter')) {
            // Content filter block — actionable message
            console.log(`\n  Content Filter: ${err.message}\n`);
            console.log('  To resolve: rephrase your prompt or adjust the content filter policy for your deployment.\n');
        }
        else if (err.message?.includes('API key') || err.message?.includes('API error')) {
            console.log(`\n  LLM Error: ${err.message}\n`);
            // Offer to reconfigure on auth-related failures
            const isAuthError = err.message?.includes('401') ||
                err.message?.includes('403') ||
                err.message?.includes('502') ||
                err.message?.includes('authenticate') ||
                err.message?.includes('Unauthorized');
            if (isAuthError && process.stdin.isTTY) {
                const answer = await new Promise((resolve) => {
                    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
                    rl.question('  Reconfigure LLM settings? (Y/n): ', (ans) => {
                        rl.close();
                        resolve(ans.trim().toLowerCase());
                    });
                });
                if (!answer || answer === 'y' || answer === 'yes') {
                    // Clear saved config so next run triggers interactive setup
                    await saveCLIConfig({});
                    console.log('  Config cleared. Run `codragraph wiki` again to reconfigure.\n');
                }
            }
        }
        else {
            console.log(`\n  Error: ${err.message}\n`);
            if (process.env.CODRAGRAPH_VERBOSE) {
                console.error(err);
            }
        }
        process.exitCode = 1;
    }
};
// ─── Gist Publishing ───────────────────────────────────────────────────
function hasGhCLI() {
    try {
        execSync('gh --version', { stdio: 'ignore' });
        return true;
    }
    catch {
        return false;
    }
}
function publishGist(htmlPath) {
    try {
        const output = execFileSync('gh', [
            'gist',
            'create',
            htmlPath,
            '--desc',
            'Repository Wiki — generated by CodraGraph',
            '--public',
        ], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
        // gh gist create prints the gist URL as the last line
        const lines = output.split('\n');
        const gistUrl = lines.find((l) => l.includes('gist.github.com')) || lines[lines.length - 1];
        if (!gistUrl || !gistUrl.includes('gist.github.com'))
            return null;
        // Build a raw viewer URL via gist.githack.com
        // gist URL format: https://gist.github.com/{user}/{id}
        const match = gistUrl.match(/gist\.github\.com\/([^/]+)\/([a-f0-9]+)/);
        let rawUrl = gistUrl;
        if (match) {
            rawUrl = `https://gistcdn.githack.com/${match[1]}/${match[2]}/raw/index.html`;
        }
        return { url: gistUrl.trim(), rawUrl };
    }
    catch {
        return null;
    }
}
async function maybePublishGist(htmlPath, gistFlag) {
    if (gistFlag === false)
        return;
    // Check that the HTML file exists
    try {
        const fs = await import('fs/promises');
        await fs.access(htmlPath);
    }
    catch {
        return;
    }
    if (!hasGhCLI()) {
        if (gistFlag) {
            console.log('  GitHub CLI (gh) is not installed. Cannot publish gist.');
            console.log('  Install it: https://cli.github.com\n');
        }
        return;
    }
    let shouldPublish = !!gistFlag;
    if (!shouldPublish && process.stdin.isTTY) {
        const answer = await new Promise((resolve) => {
            const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
            rl.question('  Publish wiki as a GitHub Gist for easy viewing? (Y/n): ', (ans) => {
                rl.close();
                resolve(ans.trim().toLowerCase());
            });
        });
        shouldPublish = !answer || answer === 'y' || answer === 'yes';
    }
    if (!shouldPublish)
        return;
    console.log('\n  Publishing to GitHub Gist...');
    const result = publishGist(htmlPath);
    if (result) {
        console.log(`  Gist:   ${result.url}`);
        console.log(`  Viewer: ${result.rawUrl}\n`);
    }
    else {
        console.log('  Failed to publish gist. Make sure `gh auth login` is configured.\n');
    }
}
