"use strict";
// src/cli/install/WizardIO.ts
// Sequential I/O surface for the Install Wizard 2.0.
//
// Core principle: every prompt waits for an explicit Enter. No batching, no
// silent defaults. Tests inject a CannedIO; production uses the readline-based
// implementation.
//
// Reuses MatrixTheme for visual consistency with ChatTui / ConfigTui.
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CannedIO = exports.ReadlineWizardIO = void 0;
const readline = __importStar(require("readline"));
const MatrixTheme_1 = require("../MatrixTheme");
/**
 * Production IO — wraps node:readline. Renders with Matrix theme when stdout
 * supports color; otherwise plain text.
 */
class ReadlineWizardIO {
    rl;
    ownsInterface;
    constructor(rl) {
        if (rl) {
            this.rl = rl;
            this.ownsInterface = false;
        }
        else {
            this.rl = readline.createInterface({ input: process.stdin, output: process.stdout });
            this.ownsInterface = true;
        }
    }
    close() {
        if (this.ownsInterface)
            this.rl.close();
    }
    header(line) {
        const color = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.MATRIX.head : '';
        const reset = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.ANSI.reset : '';
        process.stdout.write(`\n${color}━━━ ${line} ━━━${reset}\n\n`);
    }
    print(line) {
        const color = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.MATRIX.body : '';
        const reset = (0, MatrixTheme_1.supportsColor)() ? MatrixTheme_1.ANSI.reset : '';
        process.stdout.write(`${color}${line}${reset}\n`);
    }
    hint(line) {
        process.stdout.write(`${(0, MatrixTheme_1.paint)(line, MatrixTheme_1.MATRIX.tail)}\n`);
    }
    warn(line) {
        process.stdout.write(`${(0, MatrixTheme_1.paint)('⚠ ' + line, MatrixTheme_1.MATRIX.warn)}\n`);
    }
    err(line) {
        process.stdout.write(`${(0, MatrixTheme_1.paint)('✗ ' + line, MatrixTheme_1.MATRIX.err)}\n`);
    }
    divider() {
        const w = Math.min(process.stdout.columns || 60, 60);
        process.stdout.write(`${(0, MatrixTheme_1.paint)('─'.repeat(w), MatrixTheme_1.MATRIX.tail)}\n`);
    }
    ask(prompt) {
        return new Promise((resolve) => {
            this.rl.question(prompt, (answer) => resolve(answer));
        });
    }
    async text(prompt, defaultValue) {
        const suffix = defaultValue !== undefined && defaultValue !== ''
            ? (0, MatrixTheme_1.paint)(` [${defaultValue}]`, MatrixTheme_1.MATRIX.tail)
            : '';
        const promptStr = `${(0, MatrixTheme_1.paint)(prompt, MatrixTheme_1.MATRIX.body)}${suffix}${(0, MatrixTheme_1.paint)(': ', MatrixTheme_1.MATRIX.head)}`;
        const raw = (await this.ask(promptStr)).trim();
        if (!raw && defaultValue !== undefined)
            return defaultValue;
        return raw;
    }
    /**
     * Masked input — disables echo, accumulates chars manually, prints * per
     * character. Falls back to plain text() when stdin is not a TTY (CI/piped).
     */
    async secret(prompt) {
        if (!process.stdin.isTTY) {
            // CI / piped — accept the pasted value as-is.
            return this.text(prompt);
        }
        return new Promise((resolve) => {
            const promptStr = `${(0, MatrixTheme_1.paint)(prompt, MatrixTheme_1.MATRIX.body)}${(0, MatrixTheme_1.paint)(': ', MatrixTheme_1.MATRIX.head)}`;
            process.stdout.write(promptStr);
            let value = '';
            const previousRaw = process.stdin.isRaw === true;
            try {
                process.stdin.setRawMode(true);
            }
            catch { /* ignore */ }
            process.stdin.resume();
            process.stdin.setEncoding('utf-8');
            const onData = (chunk) => {
                for (const ch of chunk) {
                    const code = ch.charCodeAt(0);
                    // Enter (13 or 10)
                    if (code === 13 || code === 10) {
                        process.stdin.removeListener('data', onData);
                        try {
                            process.stdin.setRawMode(previousRaw);
                        }
                        catch { /* ignore */ }
                        process.stdin.pause();
                        process.stdout.write('\n');
                        resolve(value.trim());
                        return;
                    }
                    // Ctrl-C
                    if (code === 3) {
                        process.stdin.removeListener('data', onData);
                        try {
                            process.stdin.setRawMode(previousRaw);
                        }
                        catch { /* ignore */ }
                        process.stdin.pause();
                        process.stdout.write('\n');
                        process.exit(130);
                        return;
                    }
                    // Backspace
                    if (code === 127 || code === 8) {
                        if (value.length > 0) {
                            value = value.slice(0, -1);
                            process.stdout.write('\b \b');
                        }
                        continue;
                    }
                    // Printable char — append and echo asterisk
                    if (code >= 32) {
                        value += ch;
                        process.stdout.write('*');
                    }
                }
            };
            process.stdin.on('data', onData);
        });
    }
    async yesNo(prompt, defaultYes = false) {
        const hint = defaultYes ? '[Y/n]' : '[y/N]';
        const promptStr = `${(0, MatrixTheme_1.paint)(prompt, MatrixTheme_1.MATRIX.body)} ${(0, MatrixTheme_1.paint)(hint, MatrixTheme_1.MATRIX.tail)}${(0, MatrixTheme_1.paint)(': ', MatrixTheme_1.MATRIX.head)}`;
        const raw = (await this.ask(promptStr)).trim().toLowerCase();
        if (!raw)
            return defaultYes;
        if (raw === 'y' || raw === 'yes' || raw === 's' || raw === 'sim')
            return true;
        if (raw === 'n' || raw === 'no' || raw === 'não' || raw === 'nao')
            return false;
        return defaultYes;
    }
    async choice(prompt, options, defaultIndex = 0) {
        this.print(prompt);
        for (let i = 0; i < options.length; i++) {
            const marker = i === defaultIndex ? (0, MatrixTheme_1.paint)('▶', MatrixTheme_1.MATRIX.head) : ' ';
            const num = (0, MatrixTheme_1.paint)(`${i + 1})`, MatrixTheme_1.MATRIX.body);
            this.print(`  ${marker} ${num} ${options[i]}`);
        }
        const promptStr = `${(0, MatrixTheme_1.paint)(`Choice [${defaultIndex + 1}]`, MatrixTheme_1.MATRIX.body)}${(0, MatrixTheme_1.paint)(': ', MatrixTheme_1.MATRIX.head)}`;
        const raw = (await this.ask(promptStr)).trim();
        if (!raw)
            return defaultIndex;
        const parsed = Number.parseInt(raw, 10);
        if (Number.isNaN(parsed) || parsed < 1 || parsed > options.length) {
            this.warn(`Invalid choice — must be 1..${options.length}. Using default (${defaultIndex + 1}).`);
            return defaultIndex;
        }
        return parsed - 1;
    }
    async pause(prompt = 'Press Enter to continue') {
        const promptStr = `${(0, MatrixTheme_1.paint)(`▸ ${prompt}…`, MatrixTheme_1.MATRIX.tail)}`;
        await this.ask(promptStr);
    }
}
exports.ReadlineWizardIO = ReadlineWizardIO;
/**
 * Test seam — feeds canned answers from a FIFO queue. Each prompt method
 * shifts one answer off. Empty queue throws so tests fail loudly on
 * forgotten answers.
 */
class CannedIO {
    queue;
    transcript = [];
    constructor(answers) {
        this.queue = [...answers];
    }
    header(line) { this.transcript.push(`[header] ${line}`); }
    print(line) { this.transcript.push(`[print] ${(0, MatrixTheme_1.stripAnsi)(line)}`); }
    hint(line) { this.transcript.push(`[hint] ${line}`); }
    warn(line) { this.transcript.push(`[warn] ${line}`); }
    err(line) { this.transcript.push(`[err] ${line}`); }
    divider() { this.transcript.push(`[divider]`); }
    close() { }
    next(kind) {
        if (this.queue.length === 0) {
            throw new Error(`CANNED_IO_EXHAUSTED: wizard asked for "${kind}" but the queue is empty`);
        }
        return this.queue.shift();
    }
    async text(prompt, _defaultValue) {
        this.transcript.push(`[text] ${prompt}`);
        const v = this.next('text');
        if (typeof v !== 'string')
            throw new Error(`CANNED_IO_TYPE: expected string for text, got ${typeof v}`);
        return v;
    }
    async secret(prompt) {
        this.transcript.push(`[secret] ${prompt}`);
        const v = this.next('secret');
        if (typeof v !== 'string')
            throw new Error(`CANNED_IO_TYPE: expected string for secret, got ${typeof v}`);
        return v;
    }
    async yesNo(prompt, _defaultYes) {
        this.transcript.push(`[yesNo] ${prompt}`);
        const v = this.next('yesNo');
        if (typeof v !== 'boolean')
            throw new Error(`CANNED_IO_TYPE: expected boolean for yesNo, got ${typeof v}`);
        return v;
    }
    async choice(prompt, _options, _defaultIndex) {
        this.transcript.push(`[choice] ${prompt}`);
        const v = this.next('choice');
        if (typeof v !== 'number')
            throw new Error(`CANNED_IO_TYPE: expected number for choice, got ${typeof v}`);
        return v;
    }
    async pause(_prompt) {
        this.transcript.push(`[pause]`);
        // Pause consumes one boolean from the queue — usually `true` to continue.
        // This makes test queues predictable: every Enter in the wizard = one entry.
        const v = this.next('pause');
        if (typeof v !== 'boolean')
            throw new Error(`CANNED_IO_TYPE: expected boolean for pause, got ${typeof v}`);
    }
    remaining() {
        return this.queue.length;
    }
}
exports.CannedIO = CannedIO;
