import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
 * ============================================================
 *  src/cli/configWizard.tsx —— 首次启动配置向导（Ink TUI）
 * ------------------------------------------------------------
 *  当 loadProviderLayered() 返回 null（env 和 ~/.minimal-agent/config.json 都没有
 *  完整 provider 配置）时，由 Root.tsx 渲染本组件。
 *
 *  流程：
 *    Step 1: 选 provider 预设（minimax / openai / deepseek / kimi / custom）
 *    Step 2: 输入 baseURL（预设有默认值，用户可改）
 *    Step 3: 输入 apiKey（不回显，用 * 替代）
 *    Step 4: 输入 model（预设有候选）
 *    Step 5: 连接测试（POST /chat/completions max_tokens=5）
 *      - 通过 → saveConfig + onSuccess(provider) → Root 切到 App
 *      - 失败 → 显示错误 + 回到 Step 1（保留已输入的值方便修改）
 *
 *  设计取舍：
 *    - 不引第三方 prompt 库（@inquirer/prompts 等），用 Ink + useInput 手写，
 *      保持"零新增依赖"承诺。
 *    - 不做 spinner 动画，loading 时显示一行文字够用。
 *    - 不做"返回上一步"，要修改请按 ESC 退出向导重跑（简化键位逻辑）。
 *      —— 用户中途退出 = 不写文件 = 下次启动继续向导，符合需求 "直到成功为止"。
 * ============================================================
 */
import { useCallback, useMemo, useState } from 'react';
import { Box, Text, useApp, useInput } from 'ink';
import { saveConfig } from '../config/configFile.js';
// 兜底值：custom 或表外 model 都退到这里。
// 与 src/config.ts 的同名常量含义对齐（"既没 env、saved 也没填 contextWindow" 的最终兜底）。
const FALLBACK_CONTEXT_WINDOW = 128_000;
const PRESETS = [
    {
        name: 'minimax',
        label: 'MiniMax (海螺)',
        baseURL: 'https://api.minimax.chat/v1',
        models: ['MiniMax-M2.7', 'MiniMax-M1', 'abab6.5s-chat'],
        contextWindow: 204_800,
    },
    {
        // 火山方舟 ARK Coding Plan —— 一个 endpoint 后挂多个 model id，
        // model 字段大小写严格匹配（'glm-5.1' 而非 'GLM-5.1'）。
        // 部分 model（如 glm-5.1）走 draft-2020-12 严校验，靠 zodToJson 的
        // modernizeExclusiveBounds 兼容。
        name: 'volces',
        label: '火山方舟 Coding Plan (ARK)',
        baseURL: 'https://ark.cn-beijing.volces.com/api/coding/v3',
        models: [
            'doubao-seed-2.0-pro',
            'doubao-seed-2.0-code',
            'doubao-seed-code',
            'glm-5.1',
            'minimax-latest',
            'deepseek-v3.2',
            'kimi-k2.6',
        ],
        contextWindow: 204_800,
    },
    {
        name: 'deepseek',
        label: 'DeepSeek',
        baseURL: 'https://api.deepseek.com/v1',
        models: ['deepseek-chat', 'deepseek-reasoner'],
        contextWindow: 128_000,
    },
    {
        name: 'openai',
        label: 'OpenAI',
        baseURL: 'https://api.openai.com/v1',
        models: ['gpt-4o', 'gpt-4o-mini'],
        contextWindow: 128_000,
    },
    {
        name: 'moonshot',
        label: 'Moonshot (Kimi)',
        baseURL: 'https://api.moonshot.cn/v1',
        models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],
        contextWindow: 128_000,
        modelContextWindows: {
            'moonshot-v1-8k': 8_000,
            'moonshot-v1-32k': 32_000,
            'moonshot-v1-128k': 128_000,
        },
    },
    {
        name: 'custom',
        label: '自定义（手动填 baseURL）',
        baseURL: '',
        models: [],
        contextWindow: FALLBACK_CONTEXT_WINDOW,
    },
];
function resolveContextWindow(preset, model) {
    return (preset.modelContextWindows?.[model] ??
        preset.contextWindow ??
        FALLBACK_CONTEXT_WINDOW);
}
export function ConfigWizard({ onSuccess }) {
    const { exit } = useApp();
    const [step, setStep] = useState('preset');
    const [presetIndex, setPresetIndex] = useState(0);
    const [baseURL, setBaseURL] = useState('');
    const [apiKey, setApiKey] = useState('');
    const [model, setModel] = useState('');
    const [modelIndex, setModelIndex] = useState(0);
    const [draft, setDraft] = useState('');
    const [errorMsg, setErrorMsg] = useState(null);
    const preset = PRESETS[presetIndex];
    const enterStep = useCallback((next) => {
        setErrorMsg(null);
        if (next === 'baseURL')
            setDraft(baseURL || preset.baseURL);
        if (next === 'apiKey')
            setDraft('');
        if (next === 'model') {
            if (preset.models.length > 0) {
                setDraft(model || preset.models[0]);
                setModelIndex(0);
            }
            else {
                setDraft(model);
            }
        }
        setStep(next);
    }, [baseURL, model, preset]);
    // handleTest 接受 finalModel 作为参数：
    // Step 4 里调用方 `setModel(...) + handleTest()` 时，setModel 是异步的，handleTest
    // 的 closure 还引用着旧的 model state；直接传 finalModel 才能避免"首次必跑两轮"
    // —— 第一次发请求时 model 还是空串，API 报错，回到 Step 1 再走一遍才能成功。
    //
    // 测试通过后 **不立即** saveConfig + onSuccess，而是转去 Step 5（tavily）
    // 让用户决定是否一并填 Tavily key，最后在 finalize() 一次性落盘。
    const handleTest = useCallback(async (finalModel) => {
        setStep('testing');
        setErrorMsg(null);
        try {
            const url = `${baseURL.replace(/\/$/, '')}/chat/completions`;
            const resp = await fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    Authorization: `Bearer ${apiKey}`,
                },
                body: JSON.stringify({
                    model: finalModel,
                    messages: [{ role: 'user', content: 'ping' }],
                    max_tokens: 5,
                    stream: false,
                }),
            });
            if (!resp.ok) {
                const body = await resp.text().catch(() => '');
                throw new Error(`HTTP ${resp.status}: ${body.slice(0, 200) || resp.statusText}`);
            }
            const data = (await resp.json());
            if (!Array.isArray(data.choices)) {
                throw new Error('响应中没有 choices 字段，可能不是 OpenAI 兼容协议');
            }
            setDraft('');
            setStep('tavily');
        }
        catch (e) {
            setErrorMsg(e.message || '连接失败');
            setStep('error');
        }
    }, [apiKey, baseURL]);
    // 落盘 + 切到 App。tavily key 为空表示用户跳过 WebSearch 配置。
    const finalize = useCallback(async (finalModel, tavilyApiKey) => {
        const contextWindow = resolveContextWindow(preset, finalModel);
        await saveConfig({
            baseURL,
            apiKey,
            model: finalModel,
            provider: preset.name,
            contextWindow,
            tavilyApiKey,
        });
        // 立刻注入当前进程 env，让本会话内的 WebSearch 不必重启就能用
        if (tavilyApiKey) {
            process.env.TAVILY_API_KEY = tavilyApiKey;
        }
        onSuccess({
            name: preset.name,
            baseURL,
            apiKey,
            model: finalModel,
            contextWindow,
        });
    }, [apiKey, baseURL, onSuccess, preset]);
    useInput((input, key) => {
        if (key.escape || (key.ctrl && input === 'c')) {
            exit();
            return;
        }
        if (step === 'preset') {
            if (key.upArrow) {
                setPresetIndex((i) => (i - 1 + PRESETS.length) % PRESETS.length);
            }
            else if (key.downArrow) {
                setPresetIndex((i) => (i + 1) % PRESETS.length);
            }
            else if (key.return) {
                enterStep('baseURL');
            }
            return;
        }
        if (step === 'baseURL') {
            if (key.return) {
                if (draft.trim().length === 0)
                    return;
                setBaseURL(draft.trim());
                enterStep('apiKey');
            }
            else if (key.backspace || key.delete) {
                setDraft((s) => s.slice(0, -1));
            }
            else if (input && !key.meta && !key.ctrl) {
                setDraft((s) => s + input);
            }
            return;
        }
        if (step === 'apiKey') {
            if (key.return) {
                if (draft.length === 0)
                    return;
                setApiKey(draft);
                enterStep('model');
            }
            else if (key.backspace || key.delete) {
                setDraft((s) => s.slice(0, -1));
            }
            else if (input && !key.meta && !key.ctrl) {
                setDraft((s) => s + input);
            }
            return;
        }
        if (step === 'model') {
            if (preset.models.length > 0) {
                if (key.upArrow) {
                    const ni = (modelIndex - 1 + preset.models.length) % preset.models.length;
                    setModelIndex(ni);
                    setDraft(preset.models[ni]);
                }
                else if (key.downArrow) {
                    const ni = (modelIndex + 1) % preset.models.length;
                    setModelIndex(ni);
                    setDraft(preset.models[ni]);
                }
                else if (key.tab) {
                    setDraft((s) => s + '');
                }
            }
            if (key.return) {
                const finalModel = draft.trim();
                if (finalModel.length === 0)
                    return;
                setModel(finalModel);
                void handleTest(finalModel);
            }
            else if (key.backspace || key.delete) {
                setDraft((s) => s.slice(0, -1));
            }
            else if (input && !key.meta && !key.ctrl && !key.upArrow && !key.downArrow) {
                setDraft((s) => s + input);
            }
            return;
        }
        if (step === 'tavily') {
            if (key.return) {
                // 空 draft 表示用户跳过 WebSearch 配置（合法路径，不报错）
                const tavilyApiKey = draft.trim().length > 0 ? draft.trim() : undefined;
                void finalize(model, tavilyApiKey);
            }
            else if (key.backspace || key.delete) {
                setDraft((s) => s.slice(0, -1));
            }
            else if (input && !key.meta && !key.ctrl) {
                setDraft((s) => s + input);
            }
            return;
        }
        if (step === 'error') {
            if (key.return) {
                enterStep('preset');
            }
            return;
        }
    });
    const maskedApiKey = useMemo(() => '*'.repeat(Math.min(draft.length, 32)), [draft]);
    return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "cyan", bold: true, children: "minimal-agent \u00B7 \u9996\u6B21\u914D\u7F6E\u5411\u5BFC" }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "gray", children: "\u6309 ESC \u6216 Ctrl+C \u9000\u51FA\uFF08\u4E0D\u5199\u914D\u7F6E\uFF0C\u4E0B\u6B21\u542F\u52A8\u7EE7\u7EED\u5411\u5BFC\uFF09" }) }), step === 'preset' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Step 1/5 \u00B7 \u9009\u62E9 provider \u9884\u8BBE\uFF08\u2191\u2193 \u79FB\u52A8\uFF0CEnter \u786E\u8BA4\uFF09" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: PRESETS.map((p, i) => (_jsxs(Text, { color: i === presetIndex ? 'cyan' : undefined, children: [i === presetIndex ? '> ' : '  ', p.label, p.baseURL ? `  (${p.baseURL})` : ''] }, p.name))) })] })), step === 'baseURL' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Step 2/5 \u00B7 \u8F93\u5165 API base URL\uFF08\u9ED8\u8BA4\u4E3A\u9884\u8BBE\u503C\uFF0C\u53EF\u6539\uFF09" }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: _jsx(Text, { children: draft || ' ' }) }), _jsx(Text, { color: "gray", children: "Enter \u786E\u8BA4 \u00B7 Backspace \u5220\u9664" })] })), step === 'apiKey' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Step 3/5 \u00B7 \u8F93\u5165 API key\uFF08\u8F93\u5165\u5185\u5BB9\u4E0D\u4F1A\u663E\u793A\uFF09" }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: _jsx(Text, { children: maskedApiKey || ' ' }) }), _jsxs(Text, { color: "gray", children: ["Enter \u786E\u8BA4 \u00B7 Backspace \u5220\u9664\uFF08\u5DF2\u8F93\u5165 ", draft.length, " \u5B57\u7B26\uFF09"] })] })), step === 'model' && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Step 4/5 \u00B7 \u8F93\u5165\u6216\u9009\u62E9 model", preset.models.length > 0 ? '（↑↓ 切换预设，或直接编辑）' : ''] }), preset.models.length > 0 && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: preset.models.map((m, i) => (_jsxs(Text, { color: i === modelIndex ? 'cyan' : 'gray', children: [i === modelIndex ? '> ' : '  ', m] }, m))) })), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: _jsx(Text, { children: draft || ' ' }) }), _jsx(Text, { color: "gray", children: "Enter \u63D0\u4EA4\u5E76\u6D4B\u8BD5\u8FDE\u63A5" })] })), step === 'testing' && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "yellow", children: ["\u23F3 \u6B63\u5728\u6D4B\u8BD5\u8FDE\u63A5 ", baseURL, " ..."] }), _jsxs(Text, { color: "gray", children: ["model = ", model] })] })), step === 'tavily' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "green", children: "\u2713 LLM \u8FDE\u63A5\u6D4B\u8BD5\u901A\u8FC7" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Step 5/5 \u00B7 \u53EF\u9009 - Tavily API key\uFF08\u7528\u4E8E WebSearch \u5DE5\u5177\uFF09" }) }), _jsx(Box, { marginTop: 1, borderStyle: "round", borderColor: "cyan", paddingX: 1, children: _jsx(Text, { children: maskedApiKey || ' ' }) }), _jsx(Text, { color: "gray", children: "\u76F4\u63A5 Enter \u8DF3\u8FC7\uFF08\u4E4B\u540E\u53EF\u5728\u5BF9\u8BDD\u91CC\u8F93\u5165 /config \u6DFB\u52A0\uFF0C\u6216\u8BBE env TAVILY_API_KEY\uFF09" }), _jsxs(Text, { color: "gray", children: ["\u514D\u8D39 key \u7533\u8BF7\uFF1Ahttps://tavily.com/  \u00B7  \u5DF2\u8F93\u5165 ", draft.length, " \u5B57\u7B26"] })] })), step === 'error' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u2717 \u8FDE\u63A5\u6D4B\u8BD5\u5931\u8D25" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: errorMsg ?? '未知错误' }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "gray", children: "\u5DF2\u8F93\u5165\uFF1A" }) }), _jsxs(Text, { color: "gray", children: ["  provider = ", preset.name] }), _jsxs(Text, { color: "gray", children: ["  baseURL  = ", baseURL] }), _jsxs(Text, { color: "gray", children: ["  model    = ", model] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "\u6309 Enter \u56DE\u5230 Step 1 \u91CD\u8BD5 \u00B7 ESC \u9000\u51FA" }) })] }))] }));
}
