"use strict";
// src/cli/install/Multiselect.ts
// Checkbox-style multi-select widget for raw-mode terminals.
//
// Navigation: ↑↓ to move, SPACE to toggle, ENTER to confirm, ESC to cancel.
// Pre-selected items render as `[x]`, unselected as `[ ]`.
// Items can be grouped under tier headers (rendered dim, not selectable).
//
// Falls back to a comma-separated list prompt when stdin is not a TTY (CI/piped).
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.multiselect = multiselect;
const readline = __importStar(require("readline"));
const MatrixTheme_1 = require("../MatrixTheme");
/**
 * Renders the multi-select, awaits user, returns the chosen values.
 * Returns `null` if the user cancels (ESC / Ctrl-C).
 */
async function multiselect(opts) {
    // Non-TTY fallback — prompt for comma-separated label list.
    if (!process.stdin.isTTY || !process.stdout.isTTY) {
        return nonTtyFallback(opts);
    }
    // Build display order: group headers + items.
    const lines = buildLines(opts);
    // Index of selectable items in the lines array (skip headers).
    const selectableLineIndices = [];
    for (let i = 0; i < lines.length; i++) {
        if (lines[i].kind === 'item')
            selectableLineIndices.push(i);
    }
    if (selectableLineIndices.length === 0) {
        // Nothing to select — return empty array (don't block).
        return [];
    }
    let cursorSelectableIdx = 0;
    const selected = new Set();
    // Initialize preselected
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        if (line.kind === 'item' && line.preselected && !line.disabled) {
            selected.add(i);
        }
    }
    return new Promise((resolve) => {
        const previousRaw = process.stdin.isRaw === true;
        try {
            readline.emitKeypressEvents(process.stdin);
            process.stdin.setRawMode(true);
        }
        catch {
            resolve(null);
            return;
        }
        process.stdout.write(MatrixTheme_1.ANSI.hideCursor);
        let firstDraw = true;
        const draw = () => {
            const block = renderBlock(opts.title, lines, selectableLineIndices[cursorSelectableIdx], selected);
            if (!firstDraw) {
                const lineCount = block.split('\n').length;
                process.stdout.write(`\x1b[${lineCount}A`);
            }
            firstDraw = false;
            process.stdout.write(block);
        };
        const cleanup = () => {
            process.stdin.removeListener('keypress', onKey);
            try {
                process.stdin.setRawMode(previousRaw);
            }
            catch { /* ignore */ }
            process.stdout.write(MatrixTheme_1.ANSI.showCursor);
            process.stdout.write(MatrixTheme_1.ANSI.reset);
        };
        const onKey = (_str, key) => {
            if (!key)
                return;
            if ((key.ctrl && key.name === 'c') || key.name === 'escape') {
                cleanup();
                process.stdout.write('\n');
                resolve(null);
                return;
            }
            if (key.name === 'up' || key.name === 'k') {
                cursorSelectableIdx = (cursorSelectableIdx - 1 + selectableLineIndices.length) % selectableLineIndices.length;
                draw();
                return;
            }
            if (key.name === 'down' || key.name === 'j') {
                cursorSelectableIdx = (cursorSelectableIdx + 1) % selectableLineIndices.length;
                draw();
                return;
            }
            if (key.name === 'space') {
                const lineIdx = selectableLineIndices[cursorSelectableIdx];
                const line = lines[lineIdx];
                if (line.kind === 'item' && !line.disabled) {
                    if (selected.has(lineIdx)) {
                        selected.delete(lineIdx);
                    }
                    else {
                        const max = opts.maxSelections ?? lines.length;
                        if (selected.size < max)
                            selected.add(lineIdx);
                    }
                    draw();
                }
                return;
            }
            if (key.name === 'a') {
                // Select all (skip disabled)
                for (let i = 0; i < lines.length; i++) {
                    const ln = lines[i];
                    if (ln.kind === 'item' && !ln.disabled)
                        selected.add(i);
                }
                draw();
                return;
            }
            if (key.name === 'n') {
                // Select none
                selected.clear();
                draw();
                return;
            }
            if (key.name === 'return') {
                const min = opts.minSelections ?? 0;
                if (selected.size < min) {
                    // Briefly warn — redraw with a warning line appended.
                    process.stdout.write(`\n${(0, MatrixTheme_1.paint)(`✗ pick at least ${min}`, MatrixTheme_1.MATRIX.err)}\n`);
                    // Reset firstDraw so we re-render fully below the warning.
                    firstDraw = true;
                    draw();
                    return;
                }
                cleanup();
                process.stdout.write('\n');
                const values = [];
                for (let i = 0; i < lines.length; i++) {
                    if (selected.has(i)) {
                        const ln = lines[i];
                        if (ln.kind === 'item')
                            values.push(ln.value);
                    }
                }
                resolve(values);
                return;
            }
        };
        process.stdin.on('keypress', onKey);
        draw();
    });
}
function buildLines(opts) {
    const grouped = new Map();
    const ungrouped = [];
    for (const item of opts.items) {
        if (item.group) {
            const arr = grouped.get(item.group) || [];
            arr.push(item);
            grouped.set(item.group, arr);
        }
        else {
            ungrouped.push(item);
        }
    }
    const order = opts.groupOrder ?? Array.from(grouped.keys());
    const labels = opts.groupLabels ?? {};
    const lines = [];
    for (const group of order) {
        const items = grouped.get(group);
        if (!items || items.length === 0)
            continue;
        lines.push({ kind: 'header', label: labels[group] || group });
        for (const item of items) {
            lines.push({
                kind: 'item',
                label: item.label,
                value: item.value,
                preselected: item.preselected,
                disabled: item.disabled,
                disabledReason: item.disabledReason,
                description: item.description,
            });
        }
    }
    for (const item of ungrouped) {
        lines.push({
            kind: 'item',
            label: item.label,
            value: item.value,
            preselected: item.preselected,
            disabled: item.disabled,
            disabledReason: item.disabledReason,
            description: item.description,
        });
    }
    return lines;
}
function renderBlock(title, lines, cursorLineIdx, selected) {
    const useColor = (0, MatrixTheme_1.supportsColor)();
    const head = useColor ? MatrixTheme_1.MATRIX.head : '';
    const body = useColor ? MatrixTheme_1.MATRIX.body : '';
    const tail = useColor ? MatrixTheme_1.MATRIX.tail : '';
    const reset = useColor ? MatrixTheme_1.ANSI.reset : '';
    const out = [];
    out.push(`${head}${title}${reset}`);
    out.push(`${tail}↑↓ navigate · SPACE toggle · A all · N none · ENTER confirm · ESC cancel${reset}`);
    out.push('');
    for (let i = 0; i < lines.length; i++) {
        const ln = lines[i];
        if (ln.kind === 'header') {
            out.push(`  ${tail}── ${ln.label} ──${reset}`);
            continue;
        }
        const isCursor = i === cursorLineIdx;
        const arrow = isCursor ? '▶' : ' ';
        const checked = selected.has(i) ? '[x]' : '[ ]';
        const color = ln.disabled ? tail : (isCursor ? head : body);
        const disabledNote = ln.disabled && ln.disabledReason
            ? ` ${tail}(${ln.disabledReason})${reset}`
            : '';
        const description = ln.description ? ` ${tail}— ${ln.description}${reset}` : '';
        out.push(`  ${color}${arrow} ${checked} ${ln.label}${reset}${description}${disabledNote}`);
    }
    out.push('');
    return out.join('\n');
}
async function nonTtyFallback(opts) {
    // Headless mode — print the menu, accept comma-separated indices, exit.
    process.stdout.write(`${opts.title}\n`);
    const lines = buildLines(opts);
    const selectable = lines.filter(l => l.kind === 'item');
    for (let i = 0; i < selectable.length; i++) {
        const ln = selectable[i];
        const pre = ln.preselected ? '(pre)' : '';
        process.stdout.write(`  ${i + 1}) ${ln.label} ${pre}\n`);
    }
    // Default = preselected
    const defaults = selectable
        .map((ln, i) => ({ ln, i }))
        .filter(({ ln }) => ln.preselected && !ln.disabled)
        .map(({ i }) => i + 1)
        .join(',');
    process.stdout.write(`Comma-separated indices [${defaults || 'none'}]: `);
    // Read one line from stdin synchronously is hard — just use defaults.
    // Tests use CannedIO; real CI usage is rare for multiselect.
    const chosen = selectable
        .map((ln, i) => ({ ln, i }))
        .filter(({ ln }) => ln.preselected && !ln.disabled)
        .map(({ ln }) => ln.value);
    return chosen;
}
