using System.Net.Http;
using System.Text;
using System.Text.Json;
using AxCopilot.Models;
using AxCopilot.Services.Agent;

namespace AxCopilot.Services;

public partial class LlmService
{
    // ─── OpenAI Compatible (Ollama / vLLM) Function Calling ──────────

    private async Task<List<ContentBlock>> SendOpenAiWithToolsAsync(
        List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools, CancellationToken ct)
    {
        var llm = _settings.Settings.Llm;
        var activeService = ResolveService();
        var body = BuildOpenAiToolBody(messages, tools);

        // 등록 모델의 커스텀 엔드포인트 우선 사용 (ResolveServerInfo)
        var (resolvedEp, _) = ResolveServerInfo();
        var endpoint = string.IsNullOrEmpty(resolvedEp)
            ? ResolveEndpointForService(activeService)
            : resolvedEp;

        var url = activeService.ToLowerInvariant() == "ollama"
            ? endpoint.TrimEnd('/') + "/api/chat"
            : endpoint.TrimEnd('/') + "/v1/chat/completions";
        var json = JsonSerializer.Serialize(body);

        using var req = new HttpRequestMessage(HttpMethod.Post, url)
        {
            Content = new StringContent(json, Encoding.UTF8, "application/json")
        };
        // CP4D 또는 Bearer 인증 적용
        await ApplyAuthHeaderAsync(req, ct);

        using var resp = await _http.SendAsync(req, ct);
        if (!resp.IsSuccessStatusCode)
        {
            var errBody = await resp.Content.ReadAsStringAsync(ct);
            var detail = ExtractErrorDetail(errBody);
            LogService.Warn($"[ToolUse] {activeService} API 오류 ({resp.StatusCode}): {errBody}");

            // 400 BadRequest → 도구 없이 일반 응답으로 폴백 시도
            if ((int)resp.StatusCode == 400)
                throw new ToolCallNotSupportedException(
                    $"{activeService} API 오류 ({resp.StatusCode}): {detail}");

            throw new HttpRequestException($"{activeService} API 오류 ({resp.StatusCode}): {detail}");
        }

        var respJson = await resp.Content.ReadAsStringAsync(ct);
        using var doc = JsonDocument.Parse(respJson);
        var root = doc.RootElement;

        TryParseOpenAiUsage(root);

        var blocks = new List<ContentBlock>();

        // Ollama 형식: root.message
        // OpenAI 형식: root.choices[0].message
        JsonElement message;
        if (root.TryGetProperty("message", out var ollamaMsg))
            message = ollamaMsg;
        else if (root.TryGetProperty("choices", out var choices) && choices.GetArrayLength() > 0)
            message = choices[0].TryGetProperty("message", out var choiceMsg) ? choiceMsg : default;
        else
            return blocks;

        // 텍스트 응답
        if (message.TryGetProperty("content", out var content))
        {
            var text = content.GetString();
            if (!string.IsNullOrWhiteSpace(text))
                blocks.Add(new ContentBlock { Type = "text", Text = text });
        }

        // 도구 호출 (tool_calls 배열)
        if (message.TryGetProperty("tool_calls", out var toolCalls))
        {
            foreach (var tc in toolCalls.EnumerateArray())
            {
                if (!tc.TryGetProperty("function", out var func)) continue;

                // arguments: 표준(OpenAI)은 JSON 문자열, Ollama/qwen 등은 JSON 객체를 직접 반환하기도 함
                JsonElement? parsedArgs = null;
                if (func.TryGetProperty("arguments", out var argsEl))
                {
                    if (argsEl.ValueKind == JsonValueKind.String)
                    {
                        // 표준: 문자열로 감싸진 JSON → 파싱
                        try
                        {
                            using var argsDoc = JsonDocument.Parse(argsEl.GetString() ?? "{}");
                            parsedArgs = argsDoc.RootElement.Clone();
                        }
                        catch (Exception) { parsedArgs = null; }
                    }
                    else if (argsEl.ValueKind == JsonValueKind.Object || argsEl.ValueKind == JsonValueKind.Array)
                    {
                        // Ollama/qwen 방식: 이미 JSON 객체 — 그대로 사용
                        parsedArgs = argsEl.Clone();
                    }
                }

                blocks.Add(new ContentBlock
                {
                    Type = "tool_use",
                    ToolName = func.TryGetProperty("name", out var fnm) ? fnm.GetString() ?? "" : "",
                    ToolId = tc.TryGetProperty("id", out var id) ? id.GetString() ?? Guid.NewGuid().ToString("N")[..12] : Guid.NewGuid().ToString("N")[..12],
                    ToolInput = parsedArgs,
                });
            }
        }

        return blocks;
    }

    private object BuildOpenAiToolBody(List<ChatMessage> messages, IReadOnlyCollection<IAgentTool> tools)
    {
        var llm = _settings.Settings.Llm;
        var msgs = new List<object>();

        foreach (var m in messages)
        {
            // tool_result 메시지 → OpenAI tool 응답 형식
            if (m.Role == "user" && m.Content.StartsWith("{\"type\":\"tool_result\""))
            {
                try
                {
                    using var doc = JsonDocument.Parse(m.Content);
                    var root = doc.RootElement;
                    msgs.Add(new
                    {
                        role = "tool",
                        tool_call_id = root.GetProperty("tool_use_id").GetString(),
                        content = root.GetProperty("content").GetString(),
                    });
                    continue;
                }
                catch (Exception) { }
            }

            // assistant 메시지에 tool_use 블록이 포함된 경우
            if (m.Role == "assistant" && m.Content.StartsWith("{\"_tool_use_blocks\""))
            {
                try
                {
                    using var doc = JsonDocument.Parse(m.Content);
                    var blocksArr = doc.RootElement.GetProperty("_tool_use_blocks");
                    var textContent = "";
                    var toolCallsList = new List<object>();
                    foreach (var b in blocksArr.EnumerateArray())
                    {
                        var bType = b.GetProperty("type").GetString();
                        if (bType == "text")
                            textContent = b.GetProperty("text").GetString() ?? "";
                        else if (bType == "tool_use")
                        {
                            var argsJson = b.TryGetProperty("input", out var inp) ? inp.GetRawText() : "{}";
                            toolCallsList.Add(new
                            {
                                id = b.GetProperty("id").GetString() ?? "",
                                type = "function",
                                function = new
                                {
                                    name = b.GetProperty("name").GetString() ?? "",
                                    arguments = argsJson,
                                }
                            });
                        }
                    }
                    msgs.Add(new
                    {
                        role = "assistant",
                        content = string.IsNullOrEmpty(textContent) ? (string?)null : textContent,
                        tool_calls = toolCallsList,
                    });
                    continue;
                }
                catch (Exception) { }
            }

            // ── 이미지 첨부 (Vision) ──
            if (m.Role == "user" && m.Images?.Count > 0)
            {
                var contentParts = new List<object>();
                foreach (var img in m.Images)
                    contentParts.Add(new { type = "image_url", image_url = new { url = $"data:{img.MimeType};base64,{img.Base64}" } });
                contentParts.Add(new { type = "text", text = m.Content });
                msgs.Add(new { role = m.Role, content = contentParts });
            }
            else
            {
                msgs.Add(new { role = m.Role, content = m.Content });
            }
        }

        // OpenAI 도구 정의
        var toolDefs = tools.Select(t =>
        {
            // parameters 객체: required가 비어있으면 생략 (일부 Ollama 버전 호환)
            var paramDict = new Dictionary<string, object>
            {
                ["type"] = "object",
                ["properties"] = t.Parameters.Properties.ToDictionary(
                    kv => kv.Key,
                    kv => BuildPropertySchema(kv.Value, false)),
            };
            if (t.Parameters.Required is { Count: > 0 })
                paramDict["required"] = t.Parameters.Required;

            return new
            {
                type = "function",
                function = new
                {
                    name = t.Name,
                    description = t.Description,
                    parameters = paramDict,
                }
            };
        }).ToArray();

        var activeService = ResolveService();
        var activeModel = ResolveModel();
        var isOllama = activeService.Equals("ollama", StringComparison.OrdinalIgnoreCase);
        if (isOllama)
        {
            return new
            {
                model = activeModel,
                messages = msgs,
                tools = toolDefs,
                stream = false,
                options = new { temperature = llm.Temperature }
            };
        }

        return new
        {
            model = activeModel,
            messages = msgs,
            tools = toolDefs,
            stream = false,
            temperature = llm.Temperature,
            max_tokens = llm.MaxContextTokens,
        };
    }
}
