/**
 * Shared utilities for preflight steps: logging, prompts, progress spinner, PATH refresh.
 */
import chalk from 'chalk';
import { spawn as cpSpawn, spawnSync } from 'child_process';
import { createInterface } from 'readline';
// ── Structured log ───────────────────────────────────────────────────────
/** Accumulated log entries, exposed to callers via runPreflight(). */
export const preflightLog = [];
let currentStep = 'setup';
export function setCurrentStep(step) {
    currentStep = step;
}
// ── Log helpers ──────────────────────────────────────────────────────────
const LOG_STYLES = {
    info: { icon: '→', color: chalk.cyan },
    ok: { icon: '✓', color: chalk.green },
    warn: { icon: '⚠', color: chalk.yellow }
};
/**
 * @param display - If provided, this is shown to the user while `msg` is
 *                  recorded in the structured log for the agent. Use this to
 *                  keep user-facing text friendly while preserving technical
 *                  detail for the installation agent.
 */
function emit(level, msg, opts) {
    if (!opts?.silent) {
        const { icon, color } = LOG_STYLES[level];
        console.log(color(`  ${icon} `) + (opts?.display ?? msg));
    }
    preflightLog.push({ level, step: currentStep, message: msg });
}
export const log = (msg, display) => emit('info', msg, { display });
export const ok = (msg, display) => emit('ok', msg, { display });
/** Record to the structured log for the installation agent, but don't print to terminal. */
export const note = (msg) => emit('info', msg, { silent: true });
export function header(step, display) {
    setCurrentStep(step.toLowerCase().replace(/\s+/g, '-'));
    console.log();
    console.log(chalk.bold.white(`  ${display ?? step}`));
    console.log(chalk.dim('  ' + '─'.repeat(40)));
}
// ── Prompts ──────────────────────────────────────────────────────────────
/** Prompt the user via readline and return their answer. */
function prompt(text) {
    return new Promise((res) => {
        const rl = createInterface({ input: process.stdin, output: process.stdout });
        rl.question(text, (answer) => {
            rl.close();
            res(answer);
        });
    });
}
export async function promptEnter(message) {
    const border = '═'.repeat(message.length + 6);
    console.log();
    console.log(chalk.bold.greenBright(`  ╔${border}╗`));
    console.log(chalk.bold.greenBright(`  ║   ${' '.repeat(message.length)}   ║`));
    console.log(chalk.bold.greenBright(`  ║   `) +
        chalk.bold.greenBright(message) +
        chalk.bold.greenBright(`   ║`));
    console.log(chalk.bold.greenBright(`  ║   ${' '.repeat(message.length)}   ║`));
    console.log(chalk.bold.greenBright(`  ╚${border}╝`));
    console.log();
    // Blinking prompt line — toggles between bright and dim every 500ms
    let bright = true;
    const promptText = '⏎  Press Enter to continue...';
    const blink = setInterval(() => {
        const styled = bright ? chalk.bold.greenBright(promptText) : chalk.dim.green(promptText);
        process.stderr.write(`\r  ${styled} \x1b[K`);
        bright = !bright;
    }, 500);
    await prompt(`  ${chalk.bold.greenBright(promptText)} `);
    clearInterval(blink);
    process.stderr.write('\r\x1b[K');
}
export async function promptConfirm(message) {
    const answer = await prompt(`${chalk.yellow('  ⚠')} ${message} [y/N] `);
    return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
}
// ── Progress spinner ────────────────────────────────────────────────────
export const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
/**
 * Run a command asynchronously with a live spinner that shows streaming output.
 * Used for long-running installs (winget, pip) so the user sees progress.
 */
export function runWithProgress(cmd, options) {
    const label = options?.label ?? 'Working';
    const timeout = options?.timeout ?? 120_000;
    return new Promise((resolve) => {
        const child = cpSpawn(cmd, { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });
        let stdout = '';
        let stderr = '';
        let lastLine = '';
        let frame = 0;
        let done = false;
        const spinner = setInterval(() => {
            if (done)
                return;
            const icon = chalk.cyan(SPINNER_FRAMES[frame % SPINNER_FRAMES.length]);
            const status = lastLine
                ? chalk.dim(lastLine.length > 60 ? lastLine.slice(0, 57) + '...' : lastLine)
                : '';
            process.stderr.write(`\r  ${icon} ${label} ${status}\x1b[K`);
            frame++;
        }, 80);
        const timer = setTimeout(() => {
            child.kill();
            finish(false);
        }, timeout);
        function onData(stream, chunk) {
            const text = chunk.toString('utf-8');
            if (stream === 'stdout')
                stdout += text;
            else
                stderr += text;
            // Extract last non-empty line for status display
            const lines = text.split(/\r?\n/).filter((l) => l.trim());
            if (lines.length > 0) {
                lastLine = lines[lines.length - 1].trim();
            }
        }
        child.stdout?.on('data', (chunk) => onData('stdout', chunk));
        child.stderr?.on('data', (chunk) => onData('stderr', chunk));
        function finish(success) {
            if (done)
                return;
            done = true;
            clearInterval(spinner);
            clearTimeout(timer);
            // Clear spinner line
            process.stderr.write('\r\x1b[K');
            resolve({ ok: success, stdout: stdout.trim(), stderr: stderr.trim() });
        }
        child.on('close', (code) => finish(code === 0));
        child.on('error', () => finish(false));
    });
}
// ── Windows PATH refresh ────────────────────────────────────────────────
/**
 * Re-read the Windows PATH from the registry and merge it into process.env.PATH.
 * After winget installs a program (Git, Python, etc.) the system/user PATH is
 * updated, but the running Node process still has the old value. This reads the
 * current registry entries and splices in any new directories.
 */
export function refreshPath() {
    try {
        const user = spawnSync('reg', ['query', 'HKCU\\Environment', '/v', 'Path'], {
            encoding: 'utf-8',
            timeout: 5_000
        });
        const system = spawnSync('reg', [
            'query',
            'HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment',
            '/v',
            'Path'
        ], { encoding: 'utf-8', timeout: 5_000 });
        const extract = (out) => (out.match(/REG_(?:EXPAND_)?SZ\s+(.+)/i)?.[1] ?? '')
            .split(';')
            .map((p) => p.trim())
            .filter(Boolean);
        const fresh = new Set([...extract(system.stdout ?? ''), ...extract(user.stdout ?? '')]);
        const current = (process.env.PATH ?? '').split(';').filter(Boolean);
        const currentSet = new Set(current.map((p) => p.toLowerCase()));
        for (const dir of fresh) {
            if (!currentSet.has(dir.toLowerCase())) {
                current.push(dir);
            }
        }
        process.env.PATH = current.join(';');
    }
    catch {
        // Best-effort — if registry read fails, continue with stale PATH
    }
}
//# sourceMappingURL=utils.js.map