import { getRequestHeaders } from "/script.js";
import { getContext, extension_settings } from "/scripts/extensions.js";
import { amilyHelper } from '../../../core/tavern-helper/main.js';
import Options from './Options.js';
import RequestBody from './RequestBody.js';

/**
 * ModelCaller Service
 * 负责执行 API 调用逻辑，旨在替换 NccsApi 及其他散乱的请求逻辑。
 * 支持：标准直连、ST预设调用、伪流式聚合(防超时)、数据归一化。
 */
export default class ModelCaller {
    /**
     * 构造函数注入 Logger 依赖
     * @param {Object} loggerDelegate - { log: (level, msg, origin, plugin) => void }
     */
    constructor(loggerDelegate) {
        /** @type {Object} */
        this.logger = loggerDelegate;
        this.defaultHeaders = {
            'Content-Type': 'application/json'
        };
    }

    /**
     * 统一调用入口
     * @param {string} callerName - 调用者名称（日志用）
     * @param {Array} messages - 聊天消息历史
     * @param {Options} options - 配置对象实例
     * @returns {Promise<string>} - 返回归一化后的文本内容
     */
    async call(callerName, messages, options) {
        // 1. 强制类型检查
        if (!(options instanceof Options)) {
            const errorMsg = `[ModelCaller] Options must be instance of Options class.`;
            throw new TypeError(errorMsg);
        }

        // 2. 逻辑中直接使用 options 属性
        // 记录一下当前的流模式，方便调试
        this._log('info', `API Request [${options.mode}] StreamMode: ${options.fakeStream}`, callerName);

        try {
            // 统一构建请求体 DTO
            const requestBody = new RequestBody(messages, options);
            let result;

            if (options.mode === 'preset') {
                result = await this._callPreset(callerName, requestBody, options);
            } else {
                result = await this._callDirect(callerName, requestBody, options);
            }

            // 如果是流式返回，result 已经是拼接好的字符串，不需要 normalize 的部分逻辑
            // 但为了统一，我们还是传进去检查一下
            return this._normalize(result, options.fakeStream);
        } catch (error) {
            this._log('error', `Request Failed: ${error.message}`, callerName);
            throw error;
        }
    }

    // 内部日志封装
    _log(level, msg, plugin) {
        if (this.logger?.log) {
            this.logger.log(level, msg, 'ModelCaller', plugin);
        }
    }

    // ========================================================================
    // 模式一：Direct (标准直连)
    // 对应 NccsApi 中的 callNccsOpenAITest
    // ========================================================================
    async _callDirect(callerName, requestBody, options) {
        // 构建标准 OpenAI 兼容 Body
        // 目标通常是 ST 的后端代理接口
        const url = '/api/backends/chat-completions/generate';
        const payload = requestBody.toPayload(); // 使用 DTO 生成数据

        const fetchOpts = {
            method: 'POST',
            headers: { ...getRequestHeaders(), ...this.defaultHeaders },
            body: JSON.stringify(payload)
        };

        return options.fakeStream
            ? this._fetchFakeStream(url, fetchOpts)
            : this._fetchStandard(url, fetchOpts);
    }

    // ========================================================================
    // 模式二：Preset (ST预设调用)
    // 对应 NccsApi 中的 callNccsSillyTavernPreset
    // ========================================================================
    async _callPreset(callerName, requestBody, options) {
        const context = getContext();

        // 1. 记录并切换 Profile
        const originalProfile = await amilyHelper.triggerSlash('/profile');
        const targetProfile = context.extensionSettings?.connectionManager?.profiles?.find(p => p.id === options.presetId);

        if (!targetProfile) throw new Error(`Preset ID ${options.presetId} not found`);

        if (originalProfile !== targetProfile.name) {
            this._log('info', `Switching profile: ${originalProfile} -> ${targetProfile.name}`, callerName);
            const escapedName = targetProfile.name.replace(/"/g, '\\"');
            await amilyHelper.triggerSlash(`/profile await=true "${escapedName}"`);
        }

        try {
            // 2. 根据流式需求分流处理
            if (options.fakeStream) {
                // 【流式预设请求】
                // 难点：ST 的 ConnectionManagerRequestService 不暴露流。
                // 策略：切换 Profile 后，手动向生成接口发送请求。
                const url = '/api/backends/chat-completions/generate';
                
                // [修复]: 手动合并 Profile 中的关键参数，否则后端不会自动应用预设配置
                // 提取逻辑已封装至 _buildProfilePayload
                const profilePayload = this._buildProfilePayload(targetProfile);

                // 合并顺序：基础Payload(msg) < Profile预设 < 显式Params覆盖
                // toMinimalPayload 包含: messages, stream, max_tokens, ...params
                const minimal = requestBody.toMinimalPayload();
                
                const finalPayload = {
                    ...profilePayload,
                    ...minimal, 
                    ...options.params 
                };

                const fetchOpts = {
                    method: 'POST',
                    headers: { ...getRequestHeaders(), ...this.defaultHeaders },
                    body: JSON.stringify(finalPayload)
                };
                return await this._fetchFakeStream(url, fetchOpts);
            } else {
                // 【非流式预设请求】
                // 直接使用 ST 原生服务，最稳妥
                if (!context.ConnectionManagerRequestService) throw new Error('ST Request Service unavailable');
                return await context.ConnectionManagerRequestService.sendRequest(
                    targetProfile.id,
                    requestBody.messages,
                    options.maxTokens
                );
            }

        } finally {
            // 3. 恢复 Profile
            if (originalProfile) {
                try {
                    const current = await amilyHelper.triggerSlash('/profile');
                    if (originalProfile !== current) {
                        const escapedOriginal = originalProfile.replace(/"/g, '\\"');
                        await amilyHelper.triggerSlash(`/profile await=true "${escapedOriginal}"`);
                    }
                } catch (e) {
                    this._log('warn', `Failed to restore profile: ${e.message}`, callerName);
                }
            }
        }
    }

    // ========================================================================
    // 网络层核心
    // ========================================================================

    async _fetchStandard(url, opts) {
        const res = await fetch(url, opts);
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return await res.json();
    }

    // 【核心升级】：支持 SSE 解析的伪流式聚合，防 CloudFlare 超时
    async _fetchFakeStream(url, opts) {
        const res = await fetch(url, opts);
        if (!res.ok) throw new Error(`Stream HTTP ${res.status}`);
        const reader = res.body.getReader();
        const decoder = new TextDecoder();
        let fullContent = ""; // 用于存储最终拼接的纯文本
        let buffer = ""; // 用于存储未处理完的数据片段
        try {
            while (true) {
                const { done, value } = await reader.read();
                if (done) break;
                // 1. 解码当前数据包
                const chunk = decoder.decode(value, { stream: true });
                buffer += chunk;

                // 2. 处理 SSE 格式 (data: {...})
                // 以双换行符分割每一条 SSE 消息
                const lines = buffer.split('\n');

                // 保留最后一个可能不完整的片段在 buffer 中
                buffer = lines.pop();

                for (const line of lines) {
                    const trimmed = line.trim();
                    if (!trimmed || trimmed === 'data: [DONE]') continue;

                    if (trimmed.startsWith('data: ')) {
                        try {
                            const jsonStr = trimmed.substring(6); // 去掉 'data: '
                            const json = JSON.parse(jsonStr);

                            // 提取 delta content
                            const delta = json.choices?.[0]?.delta?.content;
                            if (delta) {
                                fullContent += delta;
                            }
                        } catch (e) {
                            // 忽略解析错误的行，防止因为个别丢包导致整个请求失败
                            console.warn('[ModelCaller] SSE Parse Error:', e);
                        }
                    }
                }
            }
        } finally {
            reader.releaseLock();
        }

        // 如果 fullContent 是空的，说明可能服务端根本没返回 SSE 格式，而是直接返回了纯文本或 JSON
        // 这种情况下尝试降级处理
        if (!fullContent && buffer) {
            try {
                const json = JSON.parse(buffer);
                return json; // 是标准 JSON
            } catch {
                return buffer; // 是纯文本
            }
        }

        return fullContent;
    }

    // ========================================================================
    // 数据归一化
    // ========================================================================

    _normalize(data, isFromStream = false) {
        // 如果是从流式聚合来的，它已经是一个纯字符串了，直接返回
        if (isFromStream && typeof data === 'string') {
            return data;
        }

        // 如果是 JSON 字符串则解析
        if (typeof data === 'string') {
            try { data = JSON.parse(data); } catch (e) { return data; }
        }

        // 处理 OpenAI 格式
        if (data?.choices?.[0]?.message?.content) {
            return data.choices[0].message.content.trim();
        }

        // 处理常规 content 格式
        if (data?.content) {
            return data.content.trim();
        }

        // Fallback
        return typeof data === 'object' ? JSON.stringify(data) : data;
    }

    /**
     * 辅助方法：从 Profile 对象中提取标准生成参数
     * 严格复刻 SillyTavern 原始 Payload 逻辑
     */
    _buildProfilePayload(targetProfile) {
        const context = getContext();
        
        // 1. 基础克隆
        const payload = { ...targetProfile };

        // 2. 注入运行时元数据 (这是旧版能通的关键，包含用户/角色名等)
        payload.user_name = context.name1 || 'User';
        payload.char_name = context.name2 || 'AI';
        payload.group_names = []; // 暂不处理群组
        payload.use_sysprompt = true;
        payload.type = 'quiet';
        payload.custom_prompt_post_processing = payload.custom_prompt_post_processing || 'strict';

        // 3. 规范化模型字段
        if (!payload.model) {
            payload.model = payload.openai_model || payload.claude_model || payload.mistral_model || '';
        }

        // 4. 精准对齐 URL 映射 (解决 403/400 错误的核心)
        const rawUrl = payload['api-url'] || payload['api_url'] || payload.custom_url || payload.url;
        if (rawUrl) {
            // 如果 Source 是 custom，严格遵循旧版：custom_url 有值，reverse_proxy 为空
            if (payload.chat_completion_source === 'custom') {
                payload.custom_url = rawUrl;
                payload.reverse_proxy = payload.reverse_proxy || ''; 
            } else {
                // 如果是 openai，则填充 reverse_proxy
                payload.reverse_proxy = rawUrl;
                payload.custom_url = rawUrl;
            }
            // 兼容性修补
            payload.zai_endpoint = rawUrl;
            payload.vertexai_region = rawUrl;
        }

        // 5. 补全采样参数 (严格对齐 UI 当前状态)
        const globalGenSettings = extension_settings.text_generation || {};
        const fields = ['temperature', 'max_tokens', 'top_p', 'top_k', 'min_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty'];
        fields.forEach(field => {
            if (payload[field] === undefined) {
                payload[field] = globalGenSettings[field] ?? (field === 'temperature' ? 1 : 0);
            }
        });

        // 6. 确保 Source 存在且不被错误覆盖
        if (!payload.chat_completion_source) {
            payload.chat_completion_source = 'openai';
        }

        return payload;
    }
}

