/**
 * Logger utility for Vigthoria CLI
 */
import chalk from 'chalk';
import ora from 'ora';
/**
 * Platform-aware character map.
 *
 * Modern Windows terminals (Windows Terminal, PowerShell 7, VS Code,
 * cmd.exe with chcp 65001) render Unicode box-drawing correctly.
 * Only truly legacy code-page 437/850 consoles cannot, and those are
 * vanishingly rare on Node 20+.  We now default to full Unicode
 * everywhere and only strip emoji (multi-codepoint) on Windows where
 * monospace rendering can break.
 */
const isWin = process.platform === 'win32';
export const CH = {
    info: 'ℹ',
    warn: '⚠',
    error: '✗',
    success: '✓',
    ai: isWin ? '>' : '🤖',
    user: isWin ? '$' : '👤',
    hLine: '─',
    hDouble: '═',
    vLine: '│',
    tl: '┌',
    tr: '┐',
    bl: '└',
    br: '┘',
    teeR: '├',
    teeL: '┤',
    cross: '┼',
    bullet: '•',
    cloud: isWin ? '[cloud]' : '☁️ ',
    home: isWin ? '[local]' : '🏠 ',
    rocket: isWin ? '>>' : '🚀',
    lock: isWin ? '[lock]' : '🔒',
    warnEmoji: isWin ? '[!]' : '⚠️',
    // Double-line box drawing (used for main banner)
    dTl: '╔',
    dTr: '╗',
    dBl: '╚',
    dBr: '╝',
    dH: '═',
    dV: '║',
};
/**
 * Create an ora spinner that writes to stderr so it never
 * pollutes stdout JSON output.
 *
 * IMPORTANT: On Windows PowerShell, any stderr output triggers
 * NativeCommandError styling.  The spinner animation itself is
 * tolerable, but `spinner.fail(msg)` writes the message to stderr
 * which produces ugly red PowerShell errors.
 *
 * Prefer: spinner.stop() then Logger.error(msg) — which writes to
 * stdout — instead of spinner.fail(msg).
 */
export function createSpinner(textOrOpts) {
    const opts = typeof textOrOpts === 'string' ? { text: textOrOpts } : textOrOpts;
    // Suppress spinner animation when stderr is not a TTY (piped output,
    // CI, non-interactive terminals).  The spinner object still works — its
    // .start()/.stop()/.succeed() methods are no-ops — so callers don't
    // need conditional logic.
    const isSilent = !process.stderr.isTTY;
    return ora({ ...opts, stream: process.stderr, isSilent });
}
export class Logger {
    verbose = false;
    setVerbose(verbose) {
        this.verbose = verbose;
    }
    debug(...args) {
        if (this.verbose) {
            console.log(chalk.gray('[DEBUG]'), ...args);
        }
    }
    info(...args) {
        console.log(chalk.blue(CH.info), ...args);
    }
    warn(...args) {
        console.log(chalk.yellow(CH.warn), ...args);
    }
    error(...args) {
        // Write error messages to stdout (not stderr) to avoid triggering
        // PowerShell NativeCommandError styling.  The red ✗ prefix already
        // signals an error visually; stderr redirection is unnecessary.
        console.log(chalk.red(CH.error), ...args);
    }
    success(...args) {
        console.log(chalk.green(CH.success), ...args);
    }
    // AI response formatting
    ai(message) {
        console.log(chalk.cyan(CH.ai), message);
    }
    // User input formatting
    user(message) {
        console.log(chalk.white(CH.user), message);
    }
    // Code block
    code(code, language) {
        console.log(chalk.gray(CH.hLine.repeat(60)));
        console.log(chalk.yellow(code));
        console.log(chalk.gray(CH.hLine.repeat(60)));
    }
    // Diff output - enhanced with syntax-highlighted unified diff
    diff(added, removed) {
        removed.forEach(line => console.log(chalk.red(`- ${line}`)));
        added.forEach(line => console.log(chalk.green(`+ ${line}`)));
    }
    // Unified diff display with file context
    unifiedDiff(filePath, oldText, newText) {
        const oldLines = oldText.split('\n');
        const newLines = newText.split('\n');
        const fileName = filePath.split('/').pop() || filePath;
        console.log(chalk.bold.white(`\n  ${CH.hLine.repeat(3)} ${fileName} ${CH.hLine.repeat(Math.max(1, 50 - fileName.length))}`));
        console.log(chalk.gray(`  --- a/${filePath}`));
        console.log(chalk.gray(`  +++ b/${filePath}`));
        // Find diff ranges with context
        const contextLines = 3;
        let i = 0;
        let j = 0;
        while (i < oldLines.length || j < newLines.length) {
            if (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {
                i++;
                j++;
                continue;
            }
            // Found a difference - show context
            const startOld = Math.max(0, i - contextLines);
            const startNew = Math.max(0, j - contextLines);
            // Context before
            for (let c = startOld; c < i; c++) {
                console.log(chalk.gray(`  ${String(c + 1).padStart(4)} │ ${oldLines[c] || ''}`));
            }
            // Removed lines
            while (i < oldLines.length && (j >= newLines.length || oldLines[i] !== newLines[j])) {
                console.log(chalk.red(`  ${String(i + 1).padStart(4)} │-${oldLines[i]}`));
                i++;
                if (j < newLines.length && (i >= oldLines.length || oldLines[i] === newLines[j]))
                    break;
            }
            // Added lines
            while (j < newLines.length && (i >= oldLines.length || oldLines[i] !== newLines[j])) {
                console.log(chalk.green(`  ${String(j + 1).padStart(4)} │+${newLines[j]}`));
                j++;
                if (i < oldLines.length && oldLines[i] === newLines[j])
                    break;
            }
            // Context after
            const endCtx = Math.min(oldLines.length, i + contextLines);
            for (let c = i; c < endCtx && c < oldLines.length && j < newLines.length && oldLines[c] === newLines[j]; c++) {
                console.log(chalk.gray(`  ${String(c + 1).padStart(4)} │ ${oldLines[c] || ''}`));
                i = c + 1;
                j++;
            }
            console.log(chalk.gray(`  ${CH.hLine.repeat(55)}`));
        }
        console.log();
    }
    // Section header
    section(title) {
        // process.stdout.columns is not always a reliable indicator of how many
        // columns actually render visually (seen mismatching badly in some
        // embedded/split-pane terminal hosts on Windows) -- cap at a
        // conservative width so the separator line never overflows even when
        // the reported column count is larger than the real usable width.
        const width = Math.max(24, Number(process.stdout.columns || 80));
        const cleanTitle = String(title || '').replace(/\s+/g, ' ').trim();
        const maxTitle = Math.max(10, width - 10);
        const fittedTitle = cleanTitle.length > maxTitle ? `${cleanTitle.slice(0, maxTitle - 1)}…` : cleanTitle;
        const side = Math.max(3, Math.floor((Math.min(width, 44) - fittedTitle.length - 2) / 2));
        console.log();
        console.log(chalk.bold.cyan(`${CH.hDouble.repeat(side)} ${fittedTitle} ${CH.hDouble.repeat(side)}`));
        console.log();
    }
    // Progress
    progress(message) {
        process.stdout.write(chalk.gray(`${message}\r`));
    }
    // Clear line
    clearLine() {
        process.stdout.write('\r\x1b[K');
    }
    // Box output
    box(content, title) {
        const lines = content.split('\n');
        // Strip ANSI codes when calculating length for proper alignment
        const stripAnsi = (str) => str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
        const maxLen = Math.max(...lines.map(l => stripAnsi(l).length), title?.length || 0);
        const width = maxLen + 4;
        console.log(chalk.cyan(CH.tl + CH.hLine.repeat(width - 2) + CH.tr));
        if (title) {
            console.log(chalk.cyan(CH.vLine + ' ') + chalk.bold.white(title.padEnd(width - 4)) + chalk.cyan(' ' + CH.vLine));
            console.log(chalk.cyan(CH.teeR + CH.hLine.repeat(width - 2) + CH.teeL));
        }
        lines.forEach(line => {
            // Calculate visible length and pad accordingly
            const visibleLen = stripAnsi(line).length;
            const padding = ' '.repeat(Math.max(0, width - 4 - visibleLen));
            console.log(chalk.cyan(CH.vLine + ' ') + line + padding + chalk.cyan(' ' + CH.vLine));
        });
        console.log(chalk.cyan(CH.bl + CH.hLine.repeat(width - 2) + CH.br));
    }
    // Table output
    table(headers, rows) {
        const colWidths = headers.map((h, i) => {
            const maxData = Math.max(...rows.map(r => (r[i] || '').length));
            return Math.max(h.length, maxData);
        });
        // Header
        console.log(chalk.gray(CH.vLine + ' ') +
            headers.map((h, i) => chalk.bold(h.padEnd(colWidths[i]))).join(chalk.gray(' ' + CH.vLine + ' ')) +
            chalk.gray(' ' + CH.vLine));
        // Separator
        console.log(chalk.gray(CH.teeR + CH.hLine) +
            colWidths.map(w => CH.hLine.repeat(w)).join(chalk.gray(CH.hLine + CH.cross + CH.hLine)) +
            chalk.gray(CH.hLine + CH.teeL));
        // Rows
        rows.forEach(row => {
            console.log(chalk.gray(CH.vLine + ' ') +
                row.map((cell, i) => (cell || '').padEnd(colWidths[i])).join(chalk.gray(' ' + CH.vLine + ' ')) +
                chalk.gray(' ' + CH.vLine));
        });
    }
}
