"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PromptCanceledError = void 0;
exports.confirmIfInteractive = confirmIfInteractive;
exports.createCliPrompter = createCliPrompter;
exports.promptOrderedValues = promptOrderedValues;
const node_process_1 = require("node:process");
const catalog_1 = require("../i18n/catalog");
const importModule = Function("specifier", "return import(specifier)");
const ORDERED_VALUES_DONE = Symbol("ordered-values-done");
class PromptCanceledError extends Error {
    constructor(message = (0, catalog_1.translate)("en", "prompts.command.canceled"), options) {
        super(message, options);
        this.name = "PromptCanceledError";
    }
}
exports.PromptCanceledError = PromptCanceledError;
let promptModulePromise;
async function confirmIfInteractive(prompter, input) {
    if (!prompter.interactive) {
        return true;
    }
    const confirmed = await prompter.confirm({
        label: input.label,
        defaultValue: input.defaultValue ?? false,
    });
    if (!confirmed) {
        await input.onCanceled?.();
    }
    return confirmed;
}
function createCliPrompter(options = {}) {
    const interactive = options.interactive ?? (node_process_1.stdin.isTTY && node_process_1.stdout.isTTY);
    const loadModule = options.loadModule ?? loadPromptModule;
    const locale = options.locale ?? "en";
    return {
        interactive,
        async input(input) {
            const defaultValue = input.defaultValue ?? "";
            const validate = input.validate;
            if (!interactive) {
                validate?.(defaultValue);
                return defaultValue;
            }
            return runCliPrompt({
                interactive,
                defaultValue,
                loadModule,
                invoke: async ({ input: prompt, password }) => {
                    const promptInput = input.masked ? password : prompt;
                    return promptInput({
                        message: input.label,
                        ...(input.masked || input.defaultValue === undefined
                            ? {}
                            : { default: input.defaultValue }),
                        ...(validate === undefined
                            ? {}
                            : {
                                validate: (value) => validatePromptInput(locale, value, validate),
                            }),
                    });
                },
                locale,
            });
        },
        async select(input) {
            validateSelectRequest(input, locale);
            if (interactive && input.options.length === 1) {
                const [onlyOption] = input.options;
                if (!onlyOption) {
                    throw new Error((0, catalog_1.translate)(locale, "prompts.command.noOptionsAvailable", {
                        label: input.label,
                    }));
                }
                console.log(renderSingleSelectionMessage(locale, input.label, onlyOption.label ?? String(onlyOption.value)));
                return onlyOption.value;
            }
            return runCliPrompt({
                interactive,
                defaultValue: input.defaultValue,
                loadModule,
                invoke: async ({ select }) => select({
                    message: input.label,
                    choices: input.options.map((option) => ({
                        value: option.value,
                        ...(option.label === undefined ? {} : { name: option.label }),
                    })),
                    default: input.defaultValue,
                }),
                locale,
            });
        },
        async checkbox(input) {
            validateCheckboxRequest(input, locale);
            if (!interactive) {
                return normalizeCheckboxDefault(input);
            }
            return runCliPrompt({
                interactive,
                defaultValue: normalizeCheckboxDefault(input),
                loadModule,
                invoke: async ({ checkbox }) => checkbox({
                    message: input.label,
                    choices: input.options.map((option) => ({
                        value: option.value,
                        ...(option.label === undefined ? {} : { name: option.label }),
                        ...(input.defaultValue?.some((value) => Object.is(value, option.value))
                            ? { checked: true }
                            : {}),
                    })),
                    required: true,
                }),
                locale,
            });
        },
        async confirm(input) {
            const defaultValue = input.defaultValue ?? true;
            return runCliPrompt({
                interactive,
                defaultValue,
                loadModule,
                invoke: async ({ confirm }) => confirm({
                    message: input.label,
                    default: defaultValue,
                }),
                locale,
            });
        },
    };
}
async function promptOrderedValues(prompter, input) {
    const locale = input.locale ?? "en";
    if (input.values.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.noValuesAvailable", {
            label: input.label,
        }));
    }
    const defaults = [...input.defaultValue];
    validateOrderedValues(locale, input.label, input.values, defaults, input.allowSubset);
    if (!prompter.interactive) {
        return defaults;
    }
    const ordered = [];
    while (true) {
        const remainingValues = input.values.filter((value) => !ordered.includes(value));
        if (remainingValues.length === 0) {
            return ordered;
        }
        if (remainingValues.length === 1 &&
            !(input.allowSubset && ordered.length > 0)) {
            const [onlyValue] = remainingValues;
            if (!onlyValue) {
                throw new Error((0, catalog_1.translate)(locale, "prompts.command.noValuesAvailable", {
                    label: input.label,
                }));
            }
            console.log(renderSingleSelectionMessage(locale, input.label, renderOrderedValueLabel(input, onlyValue, remainingValues)));
            ordered.push(onlyValue);
            continue;
        }
        const defaultValue = getNextOrderedDefault(defaults, remainingValues) ?? remainingValues[0];
        if (!defaultValue) {
            throw new Error((0, catalog_1.translate)(locale, "prompts.command.noValuesAvailable", {
                label: input.label,
            }));
        }
        const options = [
            ...remainingValues.map((value) => ({
                value,
                ...(input.renderLabel === undefined
                    ? {}
                    : { label: input.renderLabel(value, remainingValues) }),
            })),
            ...(input.allowSubset && ordered.length > 0
                ? [
                    {
                        value: ORDERED_VALUES_DONE,
                        label: (0, catalog_1.translate)(locale, "prompts.command.done"),
                    },
                ]
                : []),
        ];
        const selection = await prompter.select({
            label: input.label,
            options,
            defaultValue,
        });
        if (selection === ORDERED_VALUES_DONE) {
            return ordered;
        }
        ordered.push(selection);
    }
}
function validateOrderedValues(locale, label, values, selected, allowSubset = false) {
    if (selected.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.mustIncludeAtLeastOneValue", {
            label,
        }));
    }
    const available = new Set(values);
    const seen = new Set();
    for (const value of selected) {
        if (!available.has(value)) {
            throw new Error((0, catalog_1.translate)(locale, "prompts.command.invalidEntry", {
                label: label.toLowerCase(),
                value: String(value),
                choices: values.map((entry) => String(entry)).join(", "),
            }));
        }
        if (seen.has(value)) {
            throw new Error((0, catalog_1.translate)(locale, "prompts.command.cannotContainDuplicates", {
                label,
                value: String(value),
            }));
        }
        seen.add(value);
    }
    if (!allowSubset && seen.size !== values.length) {
        const missing = values.filter((value) => !seen.has(value));
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.mustIncludeAllValues", {
            label,
            values: missing.map((value) => String(value)).join(", "),
        }));
    }
}
function getNextOrderedDefault(defaults, remainingValues) {
    return defaults.find((value) => remainingValues.includes(value));
}
function renderOrderedValueLabel(input, value, collection) {
    return input.renderLabel?.(value, collection) ?? String(value);
}
function renderSingleSelectionMessage(locale, label, value) {
    return (0, catalog_1.translate)(locale, "prompts.command.using", {
        label: label.toLowerCase(),
        value,
    });
}
function validatePromptInput(locale, value, validate) {
    try {
        validate(value);
        return true;
    }
    catch (error) {
        return error instanceof Error
            ? error.message
            : (0, catalog_1.translate)(locale, "prompts.command.invalidValue");
    }
}
function validateSelectRequest(input, locale) {
    if (input.options.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.noOptionsAvailable", {
            label: input.label,
        }));
    }
    if (!input.options.some((option) => Object.is(option.value, input.defaultValue))) {
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.invalidDefault", {
            label: input.label,
            value: String(input.defaultValue),
        }));
    }
}
function validateCheckboxRequest(input, locale) {
    if (input.options.length === 0) {
        throw new Error((0, catalog_1.translate)(locale, "prompts.command.noOptionsAvailable", {
            label: input.label,
        }));
    }
    const defaultValue = input.defaultValue ?? [];
    for (const value of defaultValue) {
        if (!input.options.some((option) => Object.is(option.value, value))) {
            throw new Error((0, catalog_1.translate)(locale, "prompts.command.invalidDefault", {
                label: input.label,
                value: String(value),
            }));
        }
    }
}
function normalizeCheckboxDefault(input) {
    const defaultValue = input.defaultValue ?? [];
    return defaultValue.filter((value) => input.options.some((option) => Object.is(option.value, value)));
}
async function loadPromptModule() {
    promptModulePromise ??= importModule("@inquirer/prompts");
    return promptModulePromise;
}
async function runCliPrompt(input) {
    if (!input.interactive) {
        return input.defaultValue;
    }
    try {
        return await input.invoke(await input.loadModule());
    }
    catch (error) {
        throw normalizePromptError(error, input.locale);
    }
}
function normalizePromptError(error, locale = "en") {
    if (error instanceof PromptCanceledError) {
        return error;
    }
    if (error instanceof Error && error.name === "ExitPromptError") {
        return new PromptCanceledError((0, catalog_1.translate)(locale, "prompts.command.canceled"), {
            cause: error,
        });
    }
    return error;
}
