import { color } from "../../components/color.js";
import { GLYPHS, symbol } from "./glyphs.js";
import { Prompt } from "./core.js";
import { limitOptions } from "./pagination.js";
import { findNonDisabled } from "./select.js";
class MultiselectPrompt extends Prompt {
    options;
    constructor(opts) {
        if (opts.options.length === 0) {
            throw new Error("Multiselect prompt requires at least one option.");
        }
        if (opts.options.every((option) => option.disabled)) {
            throw new Error("Multiselect prompt requires at least one enabled option.");
        }
        const cursor = findNonDisabled(0, 1, opts.options);
        super({
            ...opts,
            initialValue: [...(opts.initialValues ?? [])],
            validate: (value) => {
                if (opts.required && (!value || value.length === 0)) {
                    return "Please select at least one option.\nPress SPACE to select, ENTER to submit";
                }
                return undefined;
            },
            render: (prompt) => renderMultiselectPrompt(prompt, opts)
        }, false);
        this.options = opts.options;
        this._cursor = cursor;
        this.on("cursor", (action) => {
            if (action === "up" || action === "left") {
                this._cursor = findNonDisabled(this._cursor - 1, -1, this.options);
            }
            else if (action === "down" || action === "right") {
                this._cursor = findNonDisabled(this._cursor + 1, 1, this.options);
            }
            else if (action === "space") {
                this.toggleFocused();
            }
        });
        this.on("key", (key) => {
            if (key === "a") {
                this.toggleAll();
            }
            else if (key === "i") {
                this.invert();
            }
        });
    }
    get visibleOptions() {
        return this.options;
    }
    enabledOptions() {
        return this.options.filter((option) => !option.disabled);
    }
    toggleFocused() {
        const option = this.options[this.cursor];
        if (!option || option.disabled) {
            return;
        }
        this.toggleValue(option.value);
    }
    toggleValue(value) {
        const current = this.value ?? [];
        this.setValue(current.includes(value)
            ? current.filter((item) => item !== value)
            : [...current, value]);
    }
    toggleAll() {
        const enabledValues = this.enabledOptions().map((option) => option.value);
        const current = this.value ?? [];
        const allSelected = enabledValues.every((value) => current.includes(value));
        this.setValue(allSelected ? [] : enabledValues);
    }
    invert() {
        const current = this.value ?? [];
        this.setValue(this.enabledOptions().map((option) => option.value).filter((value) => !current.includes(value)));
    }
    promptNonTty() {
        if (process.env.POE_NO_PROMPT === "1") {
            return Promise.resolve(this.value ?? []);
        }
        return super.promptNonTty();
    }
}
function hasValue(values, value) {
    return (values ?? []).includes(value);
}
function renderOption(option, values, active, submitted, cancelled) {
    const selected = hasValue(values, option.value);
    const hint = option.hint ? color.dim(` (${option.hint})`) : "";
    if (submitted)
        return color.dim(option.label);
    if (cancelled)
        return color.dim.strikethrough(option.label);
    if (option.disabled)
        return `${color.gray(GLYPHS.checkboxInactive)} ${color.gray.strikethrough(option.label)}${hint}`;
    if (selected)
        return `${color.green(GLYPHS.checkboxSelected)} ${active ? option.label : color.dim(option.label)}${hint}`;
    if (active)
        return `${color.cyan(GLYPHS.checkboxActive)} ${option.label}${hint}`;
    return `${color.dim(GLYPHS.checkboxInactive)} ${color.dim(option.label)}${hint}`;
}
function renderMultiselectPrompt(prompt, opts) {
    if (prompt.state === "submit" || prompt.state === "cancel") {
        const labels = prompt.visibleOptions
            .filter((option) => hasValue(prompt.value, option.value))
            .map((option) => prompt.state === "submit" ? color.dim(option.label) : color.dim.strikethrough(option.label))
            .join(", ");
        const end = prompt.state === "submit" ? color.green(GLYPHS.barEnd) : color.red(GLYPHS.barEnd);
        return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${color.gray(GLYPHS.bar)}  ${labels}\n${end}`;
    }
    const lines = limitOptions({
        cursor: prompt.cursor,
        options: prompt.visibleOptions,
        output: opts.output ?? process.stdout,
        maxItems: opts.maxItems,
        columnPadding: 3,
        style: (option, active) => renderOption(option, prompt.value, active, false, false)
    }).map((line) => `${prompt.state === "error" ? color.yellow(GLYPHS.bar) : color.cyan(GLYPHS.bar)}  ${line}`);
    const body = [`${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}`, ...lines];
    if (prompt.state === "error") {
        body.push(`${color.yellow(GLYPHS.barEnd)}  ${color.yellow(prompt.error)}`);
    }
    else {
        body.push(color.cyan(GLYPHS.barEnd));
    }
    return body.join("\n");
}
export function multiselectPrompt(opts) {
    return new MultiselectPrompt(opts).prompt();
}
