using System.Runtime.Serialization;
using NewLife.AI.Models;

namespace NewLife.AI.Clients.OpenAI;

/// <summary>对话完成请求。兼容 OpenAI ChatCompletion 标准，同时实现 IChatRequest 可直接作为统一请求在管道中传递</summary>
public class ChatCompletionRequest : IChatRequest
{
    #region 属性
    /// <summary>模型编码</summary>
    public String? Model { get; set; }

    /// <summary>消息列表</summary>
    public IList<ChatMessage> Messages { get; set; } = [];

    /// <summary>温度。0~2，越高越随机，默认1</summary>
    public Double? Temperature { get; set; }

    /// <summary>核采样。0~1，与Temperature二选一</summary>
    public Double? TopP { get; set; }

    /// <summary>Top K</summary>
    public Int32? TopK { get; set; }

    /// <summary>最大生成令牌数</summary>
    public Int32? MaxTokens { get; set; }

    /// <summary>是否流式输出</summary>
    public Boolean Stream { get; set; }

    /// <summary>流式选项。Stream=true 时附带，请求包含用量统计</summary>
    public IDictionary<String, Object>? StreamOptions { get; set; }

    /// <summary>停止词列表</summary>
    public IList<String>? Stop { get; set; }

    /// <summary>存在惩罚。-2~2</summary>
    public Double? PresencePenalty { get; set; }

    /// <summary>频率惩罚。-2~2</summary>
    public Double? FrequencyPenalty { get; set; }

    /// <summary>可用工具列表。用于函数调用</summary>
    public IList<ChatTool>? Tools { get; set; }

    /// <summary>工具选择策略。auto/none/required 或指定工具名</summary>
    public Object? ToolChoice { get; set; }

    /// <summary>用户标识。用于追踪和限流</summary>
    public String? User { get; set; }

    /// <summary>是否启用思考模式。null=不设置，true=开启，false=关闭。仅支持的模型有效（如 Qwen3 系列、QwQ 等）</summary>
    public Boolean? EnableThinking { get; set; }

    /// <summary>响应格式。用于结构化输出，如 {"type":"json_schema","json_schema":{...}}。支持的服务商：DashScope、OpenAI 等</summary>
    public Object? ResponseFormat { get; set; }

    /// <summary>是否允许并行工具调用。null=不设置，true=允许，false=禁止</summary>
    public Boolean? ParallelToolCalls { get; set; }

    /// <summary>扩展数据。用于在中间件管道中传递非结构化的自定义上下文</summary>
    [IgnoreDataMember]
    public IDictionary<String, Object?> Items { get; set; } = new Dictionary<String, Object?>();

    /// <summary>索引器，方便访问扩展数据</summary>
    [IgnoreDataMember]
    public Object? this[String key] { get => Items.TryGetValue(key, out var value) ? value : null; set => Items[key] = value; }

    /// <summary>用户编号。内部管道传递，不参与序列化</summary>
    [IgnoreDataMember]
    public String? UserId { get; set; }

    /// <summary>会话编号。内部管道传递，不参与序列化</summary>
    [IgnoreDataMember]
    public String? ConversationId { get; set; }
    #endregion

    #region 方法
    /// <summary>从内部统一 ChatRequest 构建 OpenAI 协议请求</summary>
    /// <param name="request">内部统一请求</param>
    /// <returns>可直接 ToJson 序列化的 OpenAI 协议请求</returns>
    public static ChatCompletionRequest FromChatRequest(IChatRequest request)
    {
        var result = new ChatCompletionRequest
        {
            Model = request.Model,
            Stream = request.Stream,
            Temperature = request.Temperature,
            TopP = request.TopP,
            TopK = request.TopK,
            MaxTokens = request.MaxTokens,
            Stop = request.Stop,
            PresencePenalty = request.PresencePenalty,
            FrequencyPenalty = request.FrequencyPenalty,
            ToolChoice = request.ToolChoice,
            User = request.User,
            EnableThinking = request.EnableThinking,
            ResponseFormat = request.ResponseFormat,
            ParallelToolCalls = request.ParallelToolCalls,
            UserId = request.UserId,
            ConversationId = request.ConversationId,
        };

        if (request.Stream)
            result.StreamOptions = new Dictionary<String, Object> { ["include_usage"] = true };

        // 转换消息列表：处理 Contents（类型化多模态内容）→ Content（OpenAI 协议格式）
        var messages = new List<ChatMessage>();
        foreach (var msg in request.Messages)
        {
            if (ShouldSkipAssistantMessage(msg)) continue;

            var cm = new ChatMessage
            {
                Role = msg.Role,
                Name = msg.Name,
                ToolCallId = msg.ToolCallId,
                ToolCalls = msg.ToolCalls,
                ReasoningContent = msg.ReasoningContent,
            };

            if (msg.Contents != null && msg.Contents.Count > 0)
                cm.Content = BuildContent(msg.Contents);
            else
                cm.Content = msg.Content;

            messages.Add(cm);
        }
        result.Messages = messages;

        // 转换工具定义
        if (request.Tools != null && request.Tools.Count > 0)
            result.Tools = request.Tools;

        return result;
    }

    /// <summary>构建 OpenAI 格式的请求体字典。仅包含非空字段，避免部分模型（如 qwen-max）拒绝 null 值</summary>
    /// <param name="request">统一请求接口</param>
    /// <returns>可直接序列化为 JSON 的字典，不含 null 条目</returns>
    public static IDictionary<String, Object> BuildBody(IChatRequest request)
    {
        var dic = new Dictionary<String, Object>();

        if (!request.Model.IsNullOrEmpty()) dic["model"] = request.Model!;

        // 构建消息列表
        var messages = new List<Object>(request.Messages.Count);
        foreach (var msg in request.Messages)
        {
            if (ShouldSkipAssistantMessage(msg)) continue;

            var m = new Dictionary<String, Object> { ["role"] = msg.Role };

            // 多模态内容（Contents）优先于原始 Content 字段
            if (msg.Contents != null && msg.Contents.Count > 0)
                m["content"] = BuildContent(msg.Contents);
            else if (msg.Content != null)
                m["content"] = msg.Content;

            if (!msg.Name.IsNullOrEmpty()) m["name"] = msg.Name!;
            if (!msg.ToolCallId.IsNullOrEmpty()) m["tool_call_id"] = msg.ToolCallId!;
            // DeepSeek 思考模式：有工具调用的 assistant 轮次必须回传 reasoning_content，否则返回 400
            if (!msg.ReasoningContent.IsNullOrEmpty()) m["reasoning_content"] = msg.ReasoningContent!;

            if (msg.ToolCalls != null && msg.ToolCalls.Count > 0)
            {
                var toolCalls = new List<Object>(msg.ToolCalls.Count);
                foreach (var tc in msg.ToolCalls)
                {
                    var tcDic = new Dictionary<String, Object> { ["id"] = tc.Id!, ["type"] = tc.Type! };
                    if (tc.Function != null)
                    {
                        var args = String.IsNullOrEmpty(tc.Function.Arguments) ? "{}" : tc.Function.Arguments;
                        tcDic["function"] = new Dictionary<String, Object?> { ["name"] = tc.Function.Name, ["arguments"] = args };
                    }
                    toolCalls.Add(tcDic);
                }
                m["tool_calls"] = toolCalls;
            }
            messages.Add(m);
        }
        // 内容放到最后，方便在埋点上查看参数
        //dic["messages"] = messages;

        // stream 与 stream_options 仅在 stream=true 时写入；非流式请求不含这两个字段，避免 qwen-max 等模型的严格校验
        if (request.Stream)
        {
            dic["stream"] = true;
            dic["stream_options"] = new Dictionary<String, Object> { ["include_usage"] = true };
        }

        if (request.Temperature != null) dic["temperature"] = request.Temperature.Value;
        if (request.TopP != null) dic["top_p"] = request.TopP.Value;
        if (request.TopK != null) dic["top_k"] = request.TopK.Value;
        if (request.MaxTokens != null) dic["max_tokens"] = request.MaxTokens.Value;
        if (request.Stop != null && request.Stop.Count > 0) dic["stop"] = request.Stop;
        if (request.PresencePenalty != null) dic["presence_penalty"] = request.PresencePenalty.Value;
        if (request.FrequencyPenalty != null) dic["frequency_penalty"] = request.FrequencyPenalty.Value;
        if (request.User != null) dic["user"] = request.User;

        if (request.Tools != null && request.Tools.Count > 0)
        {
            var tools = new List<Object>(request.Tools.Count);
            foreach (var tool in request.Tools)
            {
                var t = new Dictionary<String, Object> { ["type"] = tool.Type };
                if (tool.Function != null)
                {
                    var fn = new Dictionary<String, Object?> { ["name"] = tool.Function.Name };
                    if (tool.Function.Description != null) fn["description"] = tool.Function.Description;
                    if (tool.Function.Parameters != null) fn["parameters"] = tool.Function.Parameters;
                    t["function"] = fn;
                }
                tools.Add(t);
            }
            dic["tools"] = tools;
        }
        if (request.ToolChoice != null) dic["tool_choice"] = request.ToolChoice;
        if (request.EnableThinking != null) dic["enable_thinking"] = request.EnableThinking.Value;
        if (request.ResponseFormat != null) dic["response_format"] = request.ResponseFormat;
        if (request.ParallelToolCalls != null) dic["parallel_tool_calls"] = request.ParallelToolCalls.Value;
        dic["messages"] = messages;

        return dic;
    }

    /// <summary>判断是否应跳过非法 assistant 消息。某些服务商（如 DeepSeek）要求 assistant 至少提供 content 或 tool_calls</summary>
    /// <param name="message">待检查消息</param>
    /// <returns>应跳过返回 true，否则返回 false</returns>
    private static Boolean ShouldSkipAssistantMessage(ChatMessage message)
    {
        if (!message.Role.EqualIgnoreCase("assistant")) return false;

        var hasContent = message.Contents != null && message.Contents.Count > 0 || message.Content != null;
        var hasToolCalls = message.ToolCalls != null && message.ToolCalls.Count > 0;

        return !hasContent && !hasToolCalls;
    }

    /// <summary>将 AIContent 集合转换为 OpenAI 格式的 content 字段值</summary>
    /// <param name="contents">AIContent 列表</param>
    /// <returns>字符串（单一文本）或内容数组（多模态）</returns>
    public static Object BuildContent(IList<AIContent> contents)
    {
        // 单一纯文本且无缓存标记时直接返回字符串，省去数组封装
        if (contents.Count == 1 && contents[0] is TextContent singleText && singleText.CacheControl.IsNullOrEmpty())
            return singleText.Text;

        var parts = new List<Object>(contents.Count);
        foreach (var item in contents)
        {
            if (item is TextContent text)
            {
                var textDic = new Dictionary<String, Object> { ["type"] = "text", ["text"] = text.Text };
                if (!text.CacheControl.IsNullOrEmpty())
                    textDic["cache_control"] = new Dictionary<String, Object> { ["type"] = text.CacheControl! };
                parts.Add(textDic);
            }
            else if (item is ImageContent img)
            {
                String url;
                if (img.Data != null && img.Data.Length > 0)
                    url = $"data:{img.MediaType ?? "image/jpeg"};base64,{Convert.ToBase64String(img.Data)}";
                else
                    url = img.Uri ?? "";

                var imgDic = new Dictionary<String, Object> { ["url"] = url };
                if (img.Detail != null) imgDic["detail"] = img.Detail;
                parts.Add(new Dictionary<String, Object> { ["type"] = "image_url", ["image_url"] = imgDic });
            }
            else if (item is AudioContent audio)
            {
                // Omni 模型音频输入格式：{"type":"input_audio","input_audio":{"data":"...","format":"wav"}}
                String audioData;
                if (audio.Data != null && audio.Data.Length > 0)
                    audioData = Convert.ToBase64String(audio.Data);
                else
                    audioData = audio.Uri ?? "";

                // MediaType 如 "audio/wav" → "wav"；MediaType 为空则默认 "wav"
                var mediaType = audio.MediaType ?? "audio/wav";
                var format = mediaType.Contains('/') ? mediaType.Split('/')[^1] : mediaType;

                var audioDic = new Dictionary<String, Object> { ["data"] = audioData, ["format"] = format };
                parts.Add(new Dictionary<String, Object> { ["type"] = "input_audio", ["input_audio"] = audioDic });
            }
        }
        return parts;
    }

    /// <summary>转换为内部统一的 ChatRequest</summary>
    /// <returns>等效的 ChatRequest 实例</returns>
    public ChatRequest ToChatRequest()
    {
        var messages = new List<ChatMessage>();
        if (Messages != null)
        {
            foreach (var msg in Messages)
            {
                var cm = new ChatMessage
                {
                    Role = msg.Role,
                    ToolCalls = msg.ToolCalls,
                    Contents = msg.Contents,
                };

                // Contents 已有类型化内容时直接使用；否则从 Content 解析多模态数组
                if (cm.Contents == null || cm.Contents.Count == 0)
                {
                    if (msg.Content is String str)
                        cm.Content = str;
                    else if (msg.Content != null)
                    {
                        // Content 可能是 OpenAI 格式的多模态数组（IList/JsonElement 等）
                        var contents = ChatMessage.ParseMultimodalContent(msg.Content);
                        if (contents != null && contents.Count > 0)
                            cm.Contents = contents;
                        else
                            cm.Content = msg.Content + "";
                    }
                }

                messages.Add(cm);
            }
        }

        var req = new ChatRequest()
        {
            Model = Model,
            Messages = messages,
            Stream = Stream,
            Temperature = Temperature,
            TopP = TopP,
            TopK = TopK,
            MaxTokens = MaxTokens,
            Stop = Stop,
            PresencePenalty = PresencePenalty,
            FrequencyPenalty = FrequencyPenalty,
            Tools = Tools,
            ToolChoice = ToolChoice,
            User = User,
            EnableThinking = EnableThinking,
            ResponseFormat = ResponseFormat,
            ParallelToolCalls = ParallelToolCalls,
            Items = Items,
        };

        return req;
    }
    #endregion
}
