// js/model-detector.js

// =====================
// 自定义模型检测与相关工具
// =====================

/**
 * @file js/model-detector.js
 * @description
 * 负责处理与自定义翻译模型检测相关的功能。允许用户输入 API Base URL 和 Key (通过 KeyManager 获取)，
 * 然后尝试从该 Base URL 的 `/v1/models` 端点获取可用的模型列表，并更新 UI 元素以供选择。
 * 功能也扩展到了在特定模态框或配置界面中按需检测模型。
 *
 * 主要功能:
 * - 初始化与自定义模型相关的 UI 元素 (输入框、按钮、显示区域)。
 * - 根据用户输入的 Base URL 动态更新预期的完整 API 端点显示。
 * - (旧版) `detectAvailableModels`: 针对主界面全局自定义设置区域的可用模型检测逻辑。
 * - `detectModelsForModal`: 专门为模态框或特定配置界面设计的模型检测逻辑，接收 Base URL 和 API Key 作为参数。
 * - 更新模型选择器 (`customModelId` 下拉框或 `customModelIdInput` 输入框) 以展示检测到的模型，或允许手动输入。
 * - 将检测到的可用模型列表以及用户上次选择的模型ID持久化到 localStorage，并在加载时恢复。
 * - 提供获取当前选定模型ID和完整API端点的辅助函数。
 * - 通过 `window.modelDetector` 对象暴露公共接口。
 *
 * 注意: 此文件包含两套 `window.modelDetector` 的定义，后者 (IIFE内部) 是较新的版本，
 * 前者可能是旧版或过渡版本。在维护时需注意它们之间的功能重叠和最终应该使用的版本。
 */

function appendQueryParamToUrl(urlString, param, value) {
    try {
        const url = new URL(urlString);
        url.searchParams.set(param, value);
        return url.toString();
    } catch (error) {
        const sanitized = urlString.replace(new RegExp(`([?&])${param}=[^&]*`, 'i'), '$1').replace(/[?&]$/, '');
        const separator = sanitized.includes('?') ? '&' : '?';
        return `${sanitized}${separator}${encodeURIComponent(param)}=${encodeURIComponent(value)}`;
    }
}

function normalizeOpenAIModelsUrl(baseUrlInput, endpointMode = 'auto') {
    if (!baseUrlInput || typeof baseUrlInput !== 'string') {
        throw new Error('API Base URL 不能为空');
    }
    let base = baseUrlInput.trim();
    if (!base) {
        throw new Error('API Base URL 不能为空');
    }
    base = base.replace(/\/+$/, '');
    let lower = base.toLowerCase();

    let mode = endpointMode || 'auto';
    const normalizedSegment = 'models';
    const v1Segment = `v1/${normalizedSegment}`;

    if (mode === 'manual') {
        const stripped = base
            .replace(/\/(?:v\d+\/)?chat\/completions$/i, '')
            .replace(/\/(?:v\d+\/)?messages$/i, '')
            .replace(/\/(?:v\d+\/)?completions$/i, '');
        if (stripped !== base) {
            base = stripped.replace(/\/+$/, '');
            lower = base.toLowerCase();
            mode = 'auto';
        } else {
            return base;
        }
    }

    const terminalPaths = [
        normalizedSegment,
        `/${normalizedSegment}`,
        v1Segment,
        `/${v1Segment}`
    ];

    if (terminalPaths.some(path => lower.endsWith(path))) {
        return base;
    }

    if (mode === 'chat') {
        return `${base}/${normalizedSegment}`;
    }

    if (lower.endsWith('/v1')) {
        return `${base}/${normalizedSegment}`;
    }

    return `${base}/${v1Segment}`;
}

function normalizeGeminiModelsUrl(baseUrlInput) {
    if (!baseUrlInput || typeof baseUrlInput !== 'string') {
        throw new Error('Gemini API Base URL 不能为空');
    }
    let url;
    try {
        url = new URL(baseUrlInput.trim());
    } catch (error) {
        try {
            url = new URL(`https://${baseUrlInput.trim()}`);
        } catch (_) {
            throw new Error('Gemini API Base URL 必须包含协议（例如 https://generativelanguage.googleapis.com）');
        }
    }

    url.searchParams.delete('key');

    let path = url.pathname || '';
    if (path.length > 1 && path.endsWith('/')) {
        path = path.slice(0, -1);
    }

    if (!path || path === '/') {
        path = '/v1beta/models';
    } else if (/\/models\/[^/]+$/i.test(path)) {
        path = path.replace(/\/models\/[^/]+$/i, '/models');
    } else if (!/\/models$/i.test(path)) {
        if (/\/v1beta$/i.test(path) || /\/v1$/i.test(path)) {
            path = `${path}/models`;
        } else {
            path = `${path}/v1beta/models`;
        }
    }

    url.pathname = path;
    url.search = '';
    return url.toString();
}

function mapGeminiModelsResponse(modelsArray) {
    if (!Array.isArray(modelsArray)) return [];
    const mapped = modelsArray
        .map(model => {
            if (!model) return null;
            const fullName = model.name || model.id || '';
            if (!fullName) return null;
            const normalizedId = fullName.includes('/') ? fullName.split('/').pop() : fullName;
            if (!normalizedId) return null;

            return {
                id: normalizedId,
                name: normalizedId,
                rawName: fullName,
                rawDisplayName: model.displayName || ''
            };
        })
        .filter(Boolean);

    const uniqueById = new Map();
    for (const item of mapped) {
        if (!uniqueById.has(item.id)) {
            uniqueById.set(item.id, item);
        }
    }

    return Array.from(uniqueById.values());
}

function isGeminiFormat(requestFormat, baseUrl) {
    const formatLower = (requestFormat || '').toLowerCase();
    if (formatLower === 'gemini' || formatLower === 'gemini-preview') {
        return true;
    }
    if (typeof baseUrl === 'string' && /generativelanguage\.googleapis\.com/i.test(baseUrl)) {
        return true;
    }
    return false;
}

async function performModelDetection(baseUrlInput, apiKey, requestFormat = 'openai', endpointMode = 'auto') {
    if (!baseUrlInput || typeof baseUrlInput !== 'string') {
        throw new Error('进行模型检测需要有效的 API Base URL。');
    }
    if (!apiKey) {
        throw new Error('进行模型检测需要一个 API Key。');
    }

    const treatAsGemini = isGeminiFormat(requestFormat, baseUrlInput);

    const normalizedUrl = treatAsGemini
        ? normalizeGeminiModelsUrl(baseUrlInput)
        : normalizeOpenAIModelsUrl(baseUrlInput, endpointMode);

    const requestUrl = treatAsGemini
        ? appendQueryParamToUrl(normalizedUrl, 'key', apiKey)
        : normalizedUrl;

    const headers = treatAsGemini
        ? { 'Content-Type': 'application/json' }
        : { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' };

    const response = await fetch(requestUrl, {
        method: 'GET',
        headers
    });

    if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`API 错误 (${response.status}): ${response.statusText}. ${errorText ? 'Details: ' + errorText.substring(0, 200) : ''}`);
    }

    const data = await response.json();

    if (treatAsGemini) {
        const mapped = mapGeminiModelsResponse(data.models);
        mapped.sort((a, b) => a.name.localeCompare(b.name));
        return mapped;
    }

    if (!data || !Array.isArray(data.data)) {
        throw new Error('API返回格式不符合预期');
    }

    const popularModels = ['gpt-4', 'gpt-3.5-turbo', 'grok-', 'claude-'];
    return data.data
        .filter(model => model && model.id)
        .sort((a, b) => {
            const aPriority = popularModels.some(m => a.id.includes(m));
            const bPriority = popularModels.some(m => b.id.includes(m));
            if (aPriority && !bPriority) return -1;
            if (!aPriority && bPriority) return 1;
            return a.id.localeCompare(b.id);
        })
        .map(model => ({ id: model.id, name: model.id, created: model.created }));
}

let availableModels = []; // 存储通过旧版 detectAvailableModels 函数检测到的可用模型列表。
let lastSelectedModel = ''; // 保存用户在旧版模型选择器中上次选择或输入的模型ID。

/**
 * 初始化与旧版自定义模型检测相关的UI元素。
 * 主要操作:
 * - 获取必要的 DOM 元素 (如 Base URL 输入框, 模型ID选择器/输入框, 完整端点显示区域)。
 * - 初始状态下，隐藏模型ID下拉选择框 (`customModelId`)，显示模型ID手动输入框 (`customModelIdInput`)。
 * - 为 Base URL 输入框添加 `input` 事件监听，当其值变化时调用 `updateApiEndpointDisplay` 更新完整API端点的显示。
 * - 为模型ID下拉选择框添加 `change` 事件监听，处理选择不同模型（包括"手动输入"选项）时的逻辑：
 *   - 选择"手动输入"时，显示输入框并聚焦，如果之前有选中的模型ID，则填充到输入框。
 *   - 选择列表中的具体模型时，隐藏输入框，并记录当前选择的模型ID到 `lastSelectedModel`。
 * - 调用 `loadModelsFromStorage` 尝试从本地存储加载并恢复之前检测到的模型列表和用户选择。
 * @deprecated 此函数主要服务于旧的全局自定义模型配置UI，新版可能使用 KeyManager 内部的配置或模态框。
 */
function initModelDetectorUI() {
    const customApiEndpoint = document.getElementById('customApiEndpoint');
    const customModelId = document.getElementById('customModelId');
    const customModelIdInput = document.getElementById('customModelIdInput');
    const fullApiEndpointDisplay = document.getElementById('fullApiEndpointDisplay');
    const detectModelsBtn = document.getElementById('detectModelsBtn');

    if (customModelId && customModelIdInput) {
        // 初始隐藏下拉选择框，显示输入框
        customModelId.style.display = 'none';
    }

    if (customApiEndpoint) {
        updateApiEndpointDisplay();
        // 当Base URL输入框值变化时，更新完整API端点显示
        customApiEndpoint.addEventListener('input', updateApiEndpointDisplay);
    }

    if (customModelId) {
        // 当模型选择器变化时，更新输入框值
        customModelId.addEventListener('change', function() {
            if (this.value === 'manual-input') {
                // 选择"其他模型"时，显示输入框，并聚焦
                if(customModelIdInput) customModelIdInput.style.display = 'block';
                if(customModelIdInput) customModelIdInput.focus();
                // 如果有上次选择的模型，填入输入框
                if (lastSelectedModel && lastSelectedModel !== 'manual-input') {
                    if(customModelIdInput) customModelIdInput.value = lastSelectedModel;
                }
            } else {
                // 选择列表中的模型时，隐藏输入框
                if(customModelIdInput) customModelIdInput.style.display = 'none';
                lastSelectedModel = this.value;
            }
        });
    }

    // 从本地存储加载之前的可用模型
    loadModelsFromStorage();
}

/**
 * 根据用户在 Base URL 输入框 (`customApiEndpoint`) 中输入的内容，
 * 动态更新一个用于显示完整 OpenAI 兼容 API 端点 (`fullApiEndpointDisplay`) 的文本区域。
 * 通常它会在 Base URL 后追加 `/v1/chat/completions`。
 * 如果 Base URL 为空，则显示 "-"。
 * @deprecated 此函数与旧版 UI 相关联。
 */
function updateApiEndpointDisplay() {
    const baseUrlInput = document.getElementById('customApiEndpoint');
    const fullApiEndpointDisplay = document.getElementById('fullApiEndpointDisplay');

    if (!baseUrlInput || !fullApiEndpointDisplay) return;

    const baseUrl = baseUrlInput.value.trim();

    if (baseUrl) {
        // 移除末尾的斜杠（如果有）
        const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
        fullApiEndpointDisplay.textContent = `${cleanBaseUrl}/v1/chat/completions`;
    } else {
        fullApiEndpointDisplay.textContent = '-';
    }
}

/**
 * (旧版全局模型检测功能)
 * 尝试从用户在主设置界面提供的自定义 API Base URL (`customApiEndpoint`) 和 API Key (`translationApiKeys` - 已移除或逻辑变更)
 * 来检测可用的模型列表。检测成功后会更新模型选择 UI 并将结果保存到 localStorage。
 *
 * 主要步骤:
 * 1. 获取必要的 UI 元素。
 * 2. 检查 API Key 是否提供 (此逻辑可能已过时，因为 Key 现在由 KeyManager 管理)。
 * 3. 保存当前用户在模型选择器或输入框中的值到 `lastSelectedModel`。
 * 4. 校验 Base URL 是否已输入。
 * 5. 禁用检测按钮并显示加载状态。
 * 6. 构建指向 `/v1/models` 的请求 URL。
 * 7. 使用提供的 API Key (旧逻辑) 发起 GET 请求到该 URL。
 * 8. 处理响应：
 *    - 如果请求不成功，抛出错误。
 *    - 如果响应成功但数据格式不符合预期 (不是包含 `data` 数组的 JSON)，抛出错误。
 *    - 提取 `data` 数组中的模型对象，筛选有效模型ID，并按特定优先级 (如 GPT 模型在前) 和字母顺序排序。
 * 9. 调用 `updateModelSelector` 更新 UI 中的模型选择器。
 * 10. 调用 `saveModelsToStorage` 将检测到的模型列表保存到 localStorage。
 * 11. 显示成功或失败的通知。
 * 12. 无论成功或失败，最后都恢复检测按钮的状态。
 *
 * @async
 * @deprecated 此函数依赖于现已更改或移除的全局 API Key 输入方式，并且其功能正被更模块化的方法 (如 `detectModelsForModal` 或 KeyManager 内的检测) 所取代。
 */
async function detectAvailableModels() {
    const customApiEndpoint = document.getElementById('customApiEndpoint');
    const apiKeyInput = document.getElementById('translationApiKeys');
    const modelSelector = document.getElementById('customModelId');
    const modelInput = document.getElementById('customModelIdInput');
    const detectBtn = document.getElementById('detectModelsBtn');

    if (!customApiEndpoint || !modelSelector || !modelInput) {
        showNotification('旧版自定义模型检测所需的UI元素缺失。请使用模型管理弹窗中的功能。 ', 'warning');
        console.warn('detectAvailableModels: Missing one or more required elements (customApiEndpoint, customModelId, customModelIdInput).');
        return;
    }

    if (!apiKeyInput || !apiKeyInput.value) {
         showNotification('旧版自定义模型检测需要API Key，但相关输入框已移除。请使用模型管理。 ', 'warning');
         console.warn('detectAvailableModels: translationApiKeys input not found or empty.');
         return;
    }

    const apiKey = apiKeyInput.value.trim().split('\n')[0];

    // 保存当前选择/输入的模型ID
    lastSelectedModel = modelSelector.style.display !== 'none' ?
        modelSelector.value : modelInput.value;

    if (!customApiEndpoint.value) {
        showNotification('请先输入有效的Base URL', 'error');
        return;
    }

    // API Key is now handled by KeyManager for each source/model.
    // This generic apiKey from translationApiKeys might not be relevant.
    // if (!apiKey) {
    //     showNotification('请先输入API Key', 'error');
    //     return;
    // }

    // 禁用按钮，显示加载中状态
    if (detectBtn) {
        detectBtn.disabled = true;
        detectBtn.innerHTML = '<iconify-icon icon="carbon:circle-dash" class="mr-2 animate-spin" width="16"></iconify-icon>正在检测...';
    }

    try {
        const requestFormatSelect = document.getElementById('customRequestFormat');
        const requestFormat = requestFormatSelect ? requestFormatSelect.value : 'openai';

        const detectedModels = await performModelDetection(customApiEndpoint.value.trim(), apiKey, requestFormat);
        availableModels = detectedModels.map(model => ({ ...model }));

        // 更新UI
        updateModelSelector(availableModels);

        // 保存到本地存储
        saveModelsToStorage(availableModels);

        showNotification(`成功检测到 ${availableModels.length} 个可用模型`, 'success');
    } catch (error) {
        console.error('检测模型失败:', error);
        showNotification(`检测失败: ${error.message}`, 'error');

        // 如果发生错误，保持输入框可见
        modelSelector.style.display = 'none';
        modelInput.style.display = 'block';
    } finally {
        // 恢复按钮状态
        if (detectBtn) {
            detectBtn.disabled = false;
            detectBtn.innerHTML = '<iconify-icon icon="carbon:model-alt" class="mr-2" width="16"></iconify-icon>检测可用模型';
        }
    }
}

/**
 * (旧版 UI 更新)
 * 根据提供的模型列表 (`models`) 更新主界面上的模型选择器 (`customModelId`) 和模型输入框 (`customModelIdInput`) 的状态和内容。
 *
 * 主要逻辑:
 * - 清空模型选择器的现有选项。
 * - 如果模型列表不为空:
 *   - 遍历模型列表，为每个模型创建一个 `<option>`元素并添加到选择器中。
 *   - 添加一个特殊的"其他模型" (`manual-input`) 选项到选择器末尾。
 *   - 显示模型选择器，隐藏手动输入框。
 *   - 尝试恢复 `lastSelectedModel`：如果 `lastSelectedModel` 存在且在新的模型列表中，则选中它；
 *     如果不在列表中，则选中"其他模型"并将 `lastSelectedModel` 的值填入输入框。
 * - 如果模型列表为空，则隐藏模型选择器，仅显示手动输入框。
 *
 * @param {Array<Object>} models - 检测到的可用模型对象数组，每个对象应至少包含 `id` 属性。
 * @deprecated 此函数与旧版全局自定义模型UI相关。
 */
function updateModelSelector(models) {
    const modelSelector = document.getElementById('customModelId');
    const modelInput = document.getElementById('customModelIdInput');

    if (!modelSelector || !modelInput) {
        console.warn('updateModelSelector: customModelId or customModelIdInput element not found. Cannot update selector.');
        return;
    }

    // 清空当前选项
    modelSelector.innerHTML = '';

    if (models.length > 0) {
        // 添加模型选项
        models.forEach(model => {
            const option = document.createElement('option');
            option.value = model.id;
            option.textContent = model.id;
            modelSelector.appendChild(option);
        });

        // 添加"其他模型"选项
        const manualOption = document.createElement('option');
        manualOption.value = 'manual-input';
        manualOption.textContent = '- 输入其他模型 -';
        modelSelector.appendChild(manualOption);

        // 显示下拉选择框，隐藏输入框
        modelSelector.style.display = 'block';
        modelInput.style.display = 'none';

        // 如果之前有选择的模型，尝试选中它
        if (lastSelectedModel && lastSelectedModel !== 'manual-input') {
            const option = Array.from(modelSelector.options).find(opt => opt.value === lastSelectedModel);
            if (option) {
                modelSelector.value = lastSelectedModel;
            } else {
                // 如果之前选择的模型不在列表中，选择"其他模型"并显示输入框
                modelSelector.value = 'manual-input';
                modelInput.style.display = 'block';
                modelInput.value = lastSelectedModel;
            }
        }
    } else {
        // 没有检测到模型时，仅显示输入框
        modelSelector.style.display = 'none';
        modelInput.style.display = 'block';
    }
}

/**
 * (旧版存储)
 * 将通过 `detectAvailableModels` 检测到的模型列表和检测时间保存到 localStorage。
 * - 模型列表以 JSON 字符串形式存储在 `availableCustomModels`键下。
 * - 检测时间的时间戳存储在 `lastDetectedModelTime`键下。
 *
 * @param {Array<Object>} models - 要保存的可用模型对象数组。
 * @deprecated 与旧版模型检测流程绑定。
 */
function saveModelsToStorage(models) {
    try {
        localStorage.setItem('availableCustomModels', JSON.stringify(models));
        localStorage.setItem('lastDetectedModelTime', Date.now());
    } catch (e) {
        console.error('保存模型列表到本地存储失败:', e);
    }
}

/**
 * (旧版加载)
 * 从 localStorage 加载先前保存的可用模型列表和用户最后选择的模型ID，并尝试更新相关的旧版 UI 元素。
 *
 * 主要逻辑:
 * - 检查相关的 UI 元素 (`customModelId`, `customModelIdInput`) 是否存在于当前上下文中。如果不存在，则不执行 UI 更新。
 * - 从 `localStorage` 读取 `availableCustomModels` 和 `lastDetectedModelTime`。
 * - 如果存储的模型列表存在，并且检测时间在最近7天内，则认为缓存有效，调用 `updateModelSelector` 使用这些模型更新 UI。
 * - 从 `localStorage` 读取 `lastSelectedCustomModel` 并赋值给 `lastSelectedModel` 变量。
 *
 * @deprecated 与旧版模型检测和 UI 相关。
 */
function loadModelsFromStorage() {
    const modelSelector = document.getElementById('customModelId');
    const modelInput = document.getElementById('customModelIdInput');

    // If the target elements for the model selector don't exist globally,
    // then there's no UI to update with stored models in this context.
    // So, we can skip calling updateModelSelector.
    if (!modelSelector || !modelInput) {
        // console.warn('loadModelsFromStorage: Target elements (customModelId/customModelIdInput) not found. Skipping model list update for global UI.');
        // Still try to load lastSelectedModel as it might be used elsewhere or by other logic.
        try {
            lastSelectedModel = localStorage.getItem('lastSelectedCustomModel') || '';
        } catch (e) {
            console.error('Failed to load lastSelectedCustomModel from storage:', e);
        }
        return;
    }

    try {
        const storedModels = localStorage.getItem('availableCustomModels');
        const lastDetectedTime = localStorage.getItem('lastDetectedModelTime');

        if (storedModels) {
            const models = JSON.parse(storedModels);

            // 检查是否是在过去7天内检测的
            const isRecent = lastDetectedTime &&
                (Date.now() - parseInt(lastDetectedTime)) < 7 * 24 * 60 * 60 * 1000;

            if (models.length > 0 && isRecent) {
                availableModels = models;
                updateModelSelector(models);
            }
        }

        // 从存储中加载最后选择的模型
        lastSelectedModel = localStorage.getItem('lastSelectedCustomModel') || '';
    } catch (e) {
        console.error('从本地存储加载模型列表失败:', e);
    }
}

/**
 * (旧版获取)
 * 获取当前在旧版全局自定义模型UI中选择或输入的模型ID。
 * 它会检查模型选择器 (`customModelId`) 是否可见且选中的不是"手动输入"选项。
 * 如果是，则返回选择器的值；否则，返回模型手动输入框 (`customModelIdInput`) 的值。
 *
 * @returns {string} 当前选定或输入的模型ID。如果相关UI元素不存在，则返回空字符串。
 * @deprecated 与旧版全局自定义模型UI相关。
 */
function getCurrentModelId() {
    const modelSelector = document.getElementById('customModelId');
    const modelInput = document.getElementById('customModelIdInput');

    if (!modelSelector || !modelInput) {
        console.warn('getCurrentModelId: modelSelector or modelInput not found.');
        return '';
    }

    if (modelSelector.style.display !== 'none' && modelSelector.value !== 'manual-input') {
        // 从选择器获取
        return modelSelector.value;
    } else {
        // 从输入框获取
        return modelInput.value.trim();
    }
}

/**
 * (旧版获取)
 * 根据旧版全局自定义 Base URL 输入框 (`customApiEndpoint`) 的值，构建并返回一个完整的、
 * 通常与 OpenAI 兼容的聊天模型 API 端点 (例如，追加 `/v1/chat/completions`)。
 *
 * @returns {string} 构建的完整 API 端点。如果 Base URL 输入框不存在或为空，则返回空字符串。
 * @deprecated 与旧版全局自定义模型UI相关。
 */
function getFullApiEndpoint() {
    const baseUrlInput = document.getElementById('customApiEndpoint');
    if (!baseUrlInput) {
        console.warn('getFullApiEndpoint: customApiEndpoint input not found.');
        return '';
    }
    const baseUrl = baseUrlInput.value.trim();
    if (!baseUrl) return '';

    // 移除末尾的斜杠（如果有）
    const cleanBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;
    return `${cleanBaseUrl}/v1/chat/completions`;
}

/**
 * (旧版更新配置)
 * 收集当前在旧版全局自定义模型 UI 中设置的模型 ID 和完整 API 端点，
 * 并将选择的模型 ID 保存到 localStorage (`lastSelectedCustomModel`)。
 *
 * @returns {{modelId: string, endpoint: string}} 包含当前模型ID和端点的对象。
 * @deprecated 与旧版全局自定义模型UI相关。
 */
function updateCustomApiConfig() {
    const modelId = getCurrentModelId();
    const fullEndpoint = getFullApiEndpoint();

    // 存储最后选择的模型
    if (modelId) {
        localStorage.setItem('lastSelectedCustomModel', modelId);
    }

    return {
        modelId: modelId,
        endpoint: fullEndpoint
    };
}

/**
 * 当 DOM 完全加载后，如果 `window.modelDetectorInitialized` 尚未定义，
 * 则调用 `initModelDetectorUI` 初始化旧版的模型检测器 UI，并设置标志。
 * @deprecated 依赖于旧版UI初始化。
 */
document.addEventListener('DOMContentLoaded', function() {
    if (typeof window.modelDetectorInitialized === 'undefined') {
        initModelDetectorUI();
        window.modelDetectorInitialized = true;
    }
});

/**
 * 为特定模态框或配置界面设计的模型检测函数。
 * 它向指定的 API Base URL 的 `/v1/models` 端点发起请求，使用提供的 API Key 进行认证。
 *
 * 主要步骤:
 * 1. 参数校验：确保 `baseUrl` 和 `apiKey` 已提供。
 * 2. URL 构建：清理 `baseUrl` (移除末尾斜杠)，并构建完整的 `/v1/models` 端点 URL。
 * 3. API 请求：使用 `fetch` 发送 GET 请求，携带 `Authorization: Bearer {apiKey}` 头部。
 * 4. 响应处理：
 *    - 如果响应不成功 (`!response.ok`)，尝试获取错误文本，并抛出一个包含状态码和详情的错误。
 *    - 如果响应成功但数据格式不符合预期 (例如，JSON 中没有 `data` 数组)，抛出错误。
 *    - 从 `data` 数组中提取模型信息，筛选有效模型，按特定优先级和字母顺序排序，并简化为 `{id, name}` 对象数组。
 * 5. 返回结果：返回检测到的模型对象数组。
 * 6. 错误处理：捕获任何在过程中发生的错误，并将其向上抛出，以便调用方能够处理并向用户显示。
 *
 * @async
 * @param {string} baseUrl - 要检测模型的 API 基础 URL (例如 `https://api.openai.com`)。
 * @param {string} apiKey - 用于认证的 API 密钥。
 * @returns {Promise<Array<Object>>} 返回一个承诺，解析为检测到的模型对象数组 (每个对象包含 `id` 和 `name`)。
 * @throws {Error} 如果检测过程中发生任何错误 (如网络问题、API错误、数据格式错误)。
 */
async function detectModelsForModal(baseUrl, apiKey, requestFormat = 'openai', endpointMode = 'auto') {
    if (!baseUrl) {
        throw new Error('进行模型检测需要有效的 API Base URL。');
    }
    if (!apiKey) {
        throw new Error('进行模型检测需要一个 API Key。');
    }

    try {
        return await performModelDetection(baseUrl, apiKey, requestFormat, endpointMode);
    } catch (error) {
        console.error('模型检测 (弹窗内) 失败:', error);
        throw error;
    }
}

/**
 * @global
 * @namespace modelDetector (旧版定义)
 * @description (可能已部分过时) 全局暴露的模型检测器相关函数集合。
 * 这个版本的 `window.modelDetector` 包含了与旧版全局自定义模型设置UI交互的函数。
 * @property {function} updateApiEndpointDisplay - 更新API端点显示。
 * @property {function} detectAvailableModels - (旧版) 检测可用模型。
 * @property {function} detectModelsForModal - (新版接口，也在此处暴露) 为模态框检测模型。
 * @property {function} getCurrentModelId - (旧版) 获取当前选择的模型ID。
 * @property {function} getFullApiEndpoint - (旧版) 获取完整API端点。
 * @property {function} updateCustomApiConfig - (旧版) 更新自定义API配置。
 */
window.modelDetector = {
    updateApiEndpointDisplay,
    detectAvailableModels,    // 保留旧的，用于主设置区
    detectModelsForModal,     // 新增的，用于弹窗
    getCurrentModelId,
    getFullApiEndpoint,
    updateCustomApiConfig
};

/**
 * @file 立即执行函数表达式 (IIFE) 内部的模型检测器实现。
 * 这部分似乎是较新的或重构后的模型检测逻辑，旨在提供更通用的模型检测功能。
 * 它也将其接口暴露到 `window.modelDetector`，可能会覆盖上面较旧的定义。
 */
(function () {
    /**
     * 创建一个在指定毫秒数后解析的 Promise，用于实现延迟。
     * @param {number} ms - 延迟的毫秒数。
     * @returns {Promise<void>} 在延迟结束后解析的 Promise。
     * @private
     */
    function delay(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * (IIFE内部核心函数)
     * 检测指定 API 端点支持的模型列表。
     * 它会规范化 `apiEndpoint` (确保以 `/` 结尾，移除多余的 `v1/` 等)，
     * 然后向构建好的 `/v1/models` URL 发送 GET 请求，并处理响应。
     *
     * @async
     * @private
     * @param {string} apiEndpoint - API 的基础端点 URL。
     * @param {string} apiKey - 用于认证的 API 密钥。
     * @returns {Promise<Array<Object>>} 返回一个承诺，解析为经过 `processModelsResponse` 处理后的模型对象数组。
     * @throws {Error} 如果 API 请求失败或发生其他错误。
     */
    async function detectModels(apiEndpoint, apiKey, requestFormat = 'openai', endpointMode = 'auto') {
        try {
            return await performModelDetection(apiEndpoint, apiKey, requestFormat, endpointMode);
        } catch (error) {
            console.error('检测模型时出错:', error);
            throw error;
        }
    }

    /**
     * (IIFE内部辅助函数)
     * 处理从 `/v1/models` 端点返回的原始响应数据，将其转换为统一格式的模型对象数组。
     * 支持处理多种可能的响应格式，例如：
     *  - OpenAI 格式: 响应包含一个 `data` 数组，其中每个元素是一个模型对象。
     *  - Anthropic 格式 (推测): 响应包含一个 `models` 数组。
     *  - 其他格式: 响应的 `models` 属性是一个对象，其键是模型ID。
     * 模型对象至少包含 `id` 和 `name` 属性。OpenAI 格式的模型还会尝试按 GPT 版本排序，然后按字母顺序排序。
     *
     * @private
     * @param {Object} responseData - 从 API 获取的原始 JSON 响应数据。
     * @returns {Array<Object>} 处理和规范化后的模型对象数组。
     */
    function processModelsResponse(responseData) {
        let models = [];

        if (responseData && responseData.data && Array.isArray(responseData.data)) {
            // OpenAI格式
            models = responseData.data.map(model => ({
                id: model.id,
                name: model.id,
                created: model.created,
                // 可能的其他信息
            }));

            // 根据模型名称排序
            models.sort((a, b) => {
                // 首先尝试按GPT模型版本排序
                const gptRegex = /gpt-(\d)/;
                const aMatch = a.id.match(gptRegex);
                const bMatch = b.id.match(gptRegex);

                if (aMatch && bMatch) {
                    return parseInt(bMatch[1]) - parseInt(aMatch[1]); // 新版本在前
                }

                // 然后按名称排序
                return a.id.localeCompare(b.id);
            });
        } else if (responseData && Array.isArray(responseData.models)) {
            // Anthropic格式
            models = responseData.models.map(model => ({
                id: model.id || model.name,
                name: model.name || model.id,
                // 其他Anthropic特定信息
            }));
        } else if (responseData && responseData.models && !Array.isArray(responseData.models)) {
            // 某些API可能以对象形式返回模型
            models = Object.keys(responseData.models).map(key => ({
                id: key,
                name: responseData.models[key].name || key,
                // 其他可能的信息
            }));
        }

        return models;
    }

    /**
     * (IIFE内部接口)
     * 为模态框或特定的UI交互场景检测模型。
     * 实际上是 `detectModels` 函数的一个封装，提供了专门的错误处理上下文日志。
     *
     * @async
     * @param {string} apiEndpoint - API 的基础端点 URL。
     * @param {string} apiKey - 用于认证的 API 密钥。
     * @returns {Promise<Array<Object>>} 返回一个承诺，解析为模型对象数组。
     * @throws {Error} 如果模型检测失败。
     */
    async function detectModelsForModal(apiEndpoint, apiKey, requestFormat = 'openai', endpointMode = 'auto') {
        try {
            return await detectModels(apiEndpoint, apiKey, requestFormat, endpointMode);
        } catch (error) {
            console.error('通过模态框检测模型失败:', error);
            throw error;
        }
    }

    /**
     * (IIFE内部接口)
     * 为已配置的源站点直接检测模型。
     * 也是 `detectModels` 函数的封装，用于特定场景的错误日志。
     *
     * @async
     * @param {string} apiEndpoint - 源站点的 API 基础端点 URL。
     * @param {string} apiKey - 用于认证的 API 密钥。
     * @returns {Promise<Array<Object>>} 返回一个承诺，解析为模型对象数组。
     * @throws {Error} 如果模型检测失败。
     */
    async function detectModelsForSite(apiEndpoint, apiKey, requestFormat = 'openai', endpointMode = 'auto') {
        try {
            return await detectModels(apiEndpoint, apiKey, requestFormat, endpointMode);
        } catch (error) {
            console.error('为源站点检测模型失败:', error);
            throw error;
        }
    }

    /**
     * (IIFE内部占位符/未使用)
     * 模型检测器 UI 的初始化函数。
     * 在这个 IIFE 版本的 `modelDetector` 中，此函数体为空，表明 UI 初始化可能由外部处理，
     * 或者此版本的检测器更侧重于纯粹的API交互逻辑而非直接的UI管理。
     * @private
     */
    function initModelDetectorUI() {
        // UI初始化逻辑 (在此版本中为空)
    }

    /**
     * @global
     * @namespace modelDetector (IIFE版本定义)
     * @description (较新版本) 全局暴露的模型检测器 API。
     * 此版本更侧重于通用的模型检测逻辑，可能旨在替换或增强旧版定义。
     * @property {function} detectModelsForModal - 为模态框检测模型。
     * @property {function} detectModelsForSite - 为已配置的源站点检测模型。
     * @property {function} initModelDetectorUI - (占位符) UI 初始化函数。
     */
    window.modelDetector = {
        detectModelsForModal,
        detectModelsForSite,
        initModelDetectorUI
    };
})();
