using System;
using System.IO;
using System.Text;
using System.Text.Json;
using PolyTrader.Modules.Supervisor.Agent;

namespace PolyTrader.Modules.Supervisor.Mcp
{
    /// <summary>
    /// MCP-Light (S-4): purer JSON-RPC-2.0-Handler für das Model Context Protocol über die
    /// read-only <see cref="SupervisorToolRegistry"/>. Externe KI-Clients (z. B. Claude Code)
    /// erhalten damit dieselben Analyse-Tools wie der In-App-Agent — **kein** Modell-Zugang,
    /// nur die Daten-Tür. Unterstützt: initialize, ping, tools/list, tools/call.
    /// Pur und seiteneffektfrei → unit-getestet; der HTTP-Host ist nur eine dünne Hülle.
    /// </summary>
    public static class McpJsonRpc
    {
        public const string ProtocolVersion = "2025-03-26";
        public const string ServerName = "polytrader-supervisor";
        public const string ServerVersion = "1.0";

        /// <summary>
        /// Verarbeitet eine JSON-RPC-Nachricht. Liefert die Antwort als JSON-String —
        /// oder null für Notifications (kein id) und unparsbare Eingaben ohne id.
        /// </summary>
        public static string? Handle(string requestJson, SupervisorToolRegistry registry)
        {
            JsonDocument doc;
            try { doc = JsonDocument.Parse(requestJson); }
            catch (JsonException) { return Error(null, -32700, "Parse error"); }

            using (doc)
            {
                var root = doc.RootElement;
                JsonElement? id = root.TryGetProperty("id", out var idProp) ? idProp.Clone() : (JsonElement?)null;
                string method = root.TryGetProperty("method", out var m) ? m.GetString() ?? "" : "";

                // Notifications (kein id) werden nicht beantwortet.
                if (id == null) return null;

                try
                {
                    return method switch
                    {
                        "initialize" => Result(id.Value, w =>
                        {
                            w.WriteString("protocolVersion", ProtocolVersion);
                            w.WriteStartObject("capabilities");
                            w.WriteStartObject("tools");
                            w.WriteEndObject();
                            w.WriteEndObject();
                            w.WriteStartObject("serverInfo");
                            w.WriteString("name", ServerName);
                            w.WriteString("version", ServerVersion);
                            w.WriteEndObject();
                        }),

                        "ping" => Result(id.Value, _ => { }),

                        "tools/list" => Result(id.Value, w =>
                        {
                            w.WriteStartArray("tools");
                            foreach (var tool in registry.Tools)
                            {
                                w.WriteStartObject();
                                w.WriteString("name", tool.Name);
                                w.WriteString("description", tool.Description);
                                w.WritePropertyName("inputSchema");
                                using (var schema = JsonDocument.Parse(tool.ParametersJsonSchema))
                                    schema.RootElement.WriteTo(w);
                                w.WriteEndObject();
                            }
                            w.WriteEndArray();
                        }),

                        "tools/call" => HandleToolCall(id.Value, root, registry),

                        _ => Error(id, -32601, $"Method not found: {method}")
                    };
                }
                catch (Exception ex)
                {
                    return Error(id, -32603, $"Internal error: {ex.Message}");
                }
            }
        }

        private static string HandleToolCall(JsonElement id, JsonElement root, SupervisorToolRegistry registry)
        {
            if (!root.TryGetProperty("params", out var p) || p.ValueKind != JsonValueKind.Object)
                return Error(id, -32602, "Invalid params");

            string name = p.TryGetProperty("name", out var n) ? n.GetString() ?? "" : "";
            string argsJson = p.TryGetProperty("arguments", out var a) && a.ValueKind == JsonValueKind.Object
                ? a.GetRawText() : "{}";

            string toolResult = registry.Execute(name, argsJson);
            bool isError = toolResult.StartsWith("FEHLER", StringComparison.OrdinalIgnoreCase);

            return Result(id, w =>
            {
                w.WriteStartArray("content");
                w.WriteStartObject();
                w.WriteString("type", "text");
                w.WriteString("text", toolResult);
                w.WriteEndObject();
                w.WriteEndArray();
                w.WriteBoolean("isError", isError);
            });
        }

        // ----- JSON-RPC-Hüllen -----

        private static string Result(JsonElement id, Action<Utf8JsonWriter> writeResult)
        {
            using var ms = new MemoryStream();
            using (var w = new Utf8JsonWriter(ms))
            {
                w.WriteStartObject();
                w.WriteString("jsonrpc", "2.0");
                w.WritePropertyName("id");
                id.WriteTo(w);
                w.WriteStartObject("result");
                writeResult(w);
                w.WriteEndObject();
                w.WriteEndObject();
            }
            return Encoding.UTF8.GetString(ms.ToArray());
        }

        private static string Error(JsonElement? id, int code, string message)
        {
            using var ms = new MemoryStream();
            using (var w = new Utf8JsonWriter(ms))
            {
                w.WriteStartObject();
                w.WriteString("jsonrpc", "2.0");
                w.WritePropertyName("id");
                if (id.HasValue) id.Value.WriteTo(w); else w.WriteNullValue();
                w.WriteStartObject("error");
                w.WriteNumber("code", code);
                w.WriteString("message", message);
                w.WriteEndObject();
                w.WriteEndObject();
            }
            return Encoding.UTF8.GetString(ms.ToArray());
        }
    }
}
