using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace PolyTrader.Modules.Supervisor.Agent
{
    /// <summary>Chat-Completion-Client (Function-Calling). Interface, damit der Agent testbar ist.</summary>
    public interface IChatCompletionClient
    {
        Task<ChatResponse> CompleteAsync(string model, IReadOnlyList<ChatMessage> messages,
            IReadOnlyList<SupervisorTool> tools, CancellationToken ct);
    }

    /// <summary>
    /// OpenRouter-Client (OpenAI-kompatibles Chat-Completions-Schema inkl. Tools).
    /// API-Key: Umgebungsvariable POLYTRADER_OPENROUTER_KEY, sonst gitignorierte Datei
    /// openrouter.key im App-Ordner — GETRENNT vom Trading-Key (siehe Supervisor-Konzept §5).
    /// SICHERHEIT: OpenRouter ist ein bewusst freigegebener externer Datenempfänger
    /// (Egress-Allowlist, docs/sicherheit); es werden ausschließlich Analyse-Daten der
    /// Tools gesendet, niemals Secrets.
    /// </summary>
    public sealed class OpenRouterClient : IChatCompletionClient
    {
        public const string Endpoint = "https://openrouter.ai/api/v1/chat/completions";

        private readonly HttpClient _http;
        private readonly Func<string?> _apiKeyProvider;

        public OpenRouterClient(HttpClient http, Func<string?>? apiKeyProvider = null)
        {
            _http = http;
            _apiKeyProvider = apiKeyProvider ?? DefaultApiKeyProvider;
        }

        /// <summary>Key aus env POLYTRADER_OPENROUTER_KEY, sonst aus gitignorierter openrouter.key.</summary>
        public static string? DefaultApiKeyProvider()
        {
            string? key = Environment.GetEnvironmentVariable("POLYTRADER_OPENROUTER_KEY");
            if (!string.IsNullOrWhiteSpace(key)) return key.Trim();
            string file = Path.Combine(AppContext.BaseDirectory, "openrouter.key");
            return File.Exists(file) ? File.ReadAllText(file).Trim() : null;
        }

        public async Task<ChatResponse> CompleteAsync(string model, IReadOnlyList<ChatMessage> messages,
            IReadOnlyList<SupervisorTool> tools, CancellationToken ct)
        {
            string? apiKey = _apiKeyProvider();
            if (string.IsNullOrWhiteSpace(apiKey))
                throw new InvalidOperationException(
                    "Kein OpenRouter-API-Key. Setze POLYTRADER_OPENROUTER_KEY (Umgebungsvariable) oder lege " +
                    "die Datei 'openrouter.key' in den App-Ordner (gitignored). Separater Key für den Supervisor empfohlen.");

            string body = BuildRequestBody(model, messages, tools);
            using var request = new HttpRequestMessage(HttpMethod.Post, Endpoint);
            request.Headers.Add("Authorization", $"Bearer {apiKey}");
            request.Headers.Add("X-Title", "PolyTrader Supervisor");
            request.Content = new StringContent(body, Encoding.UTF8, "application/json");

            using var response = await _http.SendAsync(request, ct);
            string json = await response.Content.ReadAsStringAsync(ct);
            if (!response.IsSuccessStatusCode)
                throw new InvalidOperationException($"OpenRouter-Fehler {(int)response.StatusCode}: {Truncate(json, 500)}");

            return ParseResponse(json);
        }

        // ----- pure, testbare Serialisierung -----

        internal static string BuildRequestBody(string model, IReadOnlyList<ChatMessage> messages, IReadOnlyList<SupervisorTool> tools)
        {
            using var ms = new MemoryStream();
            using (var w = new Utf8JsonWriter(ms))
            {
                w.WriteStartObject();
                w.WriteString("model", model);

                w.WriteStartArray("messages");
                foreach (var m in messages)
                {
                    w.WriteStartObject();
                    w.WriteString("role", m.Role);
                    if (m.Content != null) w.WriteString("content", m.Content);
                    else w.WriteNull("content");
                    if (m.ToolCalls is { Count: > 0 })
                    {
                        w.WriteStartArray("tool_calls");
                        foreach (var tc in m.ToolCalls)
                        {
                            w.WriteStartObject();
                            w.WriteString("id", tc.Id);
                            w.WriteString("type", "function");
                            w.WriteStartObject("function");
                            w.WriteString("name", tc.Name);
                            w.WriteString("arguments", tc.ArgumentsJson);
                            w.WriteEndObject();
                            w.WriteEndObject();
                        }
                        w.WriteEndArray();
                    }
                    if (m.ToolCallId != null) w.WriteString("tool_call_id", m.ToolCallId);
                    w.WriteEndObject();
                }
                w.WriteEndArray();

                if (tools.Count > 0)
                {
                    w.WriteStartArray("tools");
                    foreach (var t in tools)
                    {
                        w.WriteStartObject();
                        w.WriteString("type", "function");
                        w.WriteStartObject("function");
                        w.WriteString("name", t.Name);
                        w.WriteString("description", t.Description);
                        w.WritePropertyName("parameters");
                        using (var doc = JsonDocument.Parse(t.ParametersJsonSchema))
                            doc.RootElement.WriteTo(w);
                        w.WriteEndObject();
                        w.WriteEndObject();
                    }
                    w.WriteEndArray();
                }

                w.WriteEndObject();
            }
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        internal static ChatResponse ParseResponse(string json)
        {
            using var doc = JsonDocument.Parse(json);
            var root = doc.RootElement;
            var message = root.GetProperty("choices")[0].GetProperty("message");

            string? content = message.TryGetProperty("content", out var c) && c.ValueKind == JsonValueKind.String
                ? c.GetString() : null;

            var toolCalls = new List<ToolCall>();
            if (message.TryGetProperty("tool_calls", out var tcs) && tcs.ValueKind == JsonValueKind.Array)
            {
                foreach (var tc in tcs.EnumerateArray())
                {
                    string id = tc.TryGetProperty("id", out var idProp) ? idProp.GetString() ?? "" : "";
                    var fn = tc.GetProperty("function");
                    toolCalls.Add(new ToolCall(id,
                        fn.GetProperty("name").GetString() ?? "",
                        fn.TryGetProperty("arguments", out var a) ? a.GetString() ?? "{}" : "{}"));
                }
            }

            int promptTokens = 0, completionTokens = 0;
            if (root.TryGetProperty("usage", out var usage))
            {
                if (usage.TryGetProperty("prompt_tokens", out var pt)) promptTokens = pt.GetInt32();
                if (usage.TryGetProperty("completion_tokens", out var ctk)) completionTokens = ctk.GetInt32();
            }

            return new ChatResponse { Content = content, ToolCalls = toolCalls, PromptTokens = promptTokens, CompletionTokens = completionTokens };
        }

        private static string Truncate(string s, int max) => s.Length <= max ? s : s[..max] + "…";
    }
}
