using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using PolyTrader.Core.Analytics;
using PolyTrader.Core.Persistence;
using PolyTrader.Modules.Supervisor.Services;
using PolyTraderSharp.Services;

namespace PolyTrader.Modules.Supervisor.Agent
{
    /// <summary>
    /// Baut die Standard-Tool-Registry des Supervisors (S-2): read-only-Zugriffe auf
    /// Entscheidungsjournal, Order-Events, Trade-Log, Dossiers, JSONL-Logs, KPIs und das
    /// Architektur-Kontext-Dokument. Alle Ergebnisse als kompakte JSON-/Markdown-Strings.
    /// </summary>
    public static class SupervisorTools
    {
        private static readonly JsonSerializerOptions JsonOpts = new()
        {
            Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
        };

        public static SupervisorToolRegistry CreateRegistry(
            IDecisionJournal journal,
            IOrderEventLog orderEvents,
            ITradeLogRepository tradeLog,
            DossierService dossiers,
            PolyTrader.Modules.Supervisor.Persistence.ISupervisorCounterfactualRepository? counterfactuals = null)
        {
            var reg = new SupervisorToolRegistry();
            string logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");

            reg.Register(new SupervisorTool(
                "query_decisions",
                "Fragt das Entscheidungsjournal ab (JEDE Handelsentscheidung inkl. Ablehnungen mit Grund). " +
                "Filter optional: accountId, tokenId, reason (z.B. MaxBuyPriceExceeded), decision (Executed/Rejected/Skipped/Failed), sinceHours.",
                """{"type":"object","properties":{"accountId":{"type":"integer"},"tokenId":{"type":"string"},"reason":{"type":"string"},"decision":{"type":"string"},"sinceHours":{"type":"integer"},"limit":{"type":"integer"}}}""",
                args =>
                {
                    int limit = Math.Clamp(SupervisorToolRegistry.GetInt(args, "limit") ?? 100, 1, 500);
                    int? accountId = SupervisorToolRegistry.GetInt(args, "accountId");
                    string? tokenId = SupervisorToolRegistry.GetString(args, "tokenId");
                    string? reason = SupervisorToolRegistry.GetString(args, "reason");
                    string? decision = SupervisorToolRegistry.GetString(args, "decision");
                    int? sinceHours = SupervisorToolRegistry.GetInt(args, "sinceHours");
                    DateTime since = sinceHours.HasValue ? DateTime.UtcNow.AddHours(-sinceHours.Value) : DateTime.MinValue;

                    var rows = journal.Query(d =>
                            (accountId == null || d.AccountId == accountId) &&
                            (tokenId == null || d.TokenId == tokenId) &&
                            d.Timestamp >= since, limit * 3)
                        .Where(d => reason == null || string.Equals(d.Reason.ToString(), reason, StringComparison.OrdinalIgnoreCase))
                        .Where(d => decision == null || string.Equals(d.Decision.ToString(), decision, StringComparison.OrdinalIgnoreCase))
                        .Take(limit)
                        .Select(d => new
                        {
                            d.SignalId, ts = d.Timestamp, module = d.ModuleName, d.AccountId, d.IsDemo,
                            d.TokenId, market = d.MarketQuestion, d.Side, price = d.SignalPrice,
                            decision = d.Decision.ToString(), reason = d.Reason.ToString(), d.Message, ctx = d.ContextJson
                        });
                    return JsonSerializer.Serialize(rows, JsonOpts);
                }));

            reg.Register(new SupervisorTool(
                "query_order_events",
                "Fragt das Order-Lifecycle-Log ab (Platzierungen, CLOB-Antworten, Cancels, Leiter-Stufen). " +
                "Filter optional: accountId, tokenId, signalId, sinceHours.",
                """{"type":"object","properties":{"accountId":{"type":"integer"},"tokenId":{"type":"string"},"signalId":{"type":"string"},"sinceHours":{"type":"integer"},"limit":{"type":"integer"}}}""",
                args =>
                {
                    int limit = Math.Clamp(SupervisorToolRegistry.GetInt(args, "limit") ?? 100, 1, 500);
                    int? accountId = SupervisorToolRegistry.GetInt(args, "accountId");
                    string? tokenId = SupervisorToolRegistry.GetString(args, "tokenId");
                    string? signalId = SupervisorToolRegistry.GetString(args, "signalId");
                    int? sinceHours = SupervisorToolRegistry.GetInt(args, "sinceHours");
                    DateTime since = sinceHours.HasValue ? DateTime.UtcNow.AddHours(-sinceHours.Value) : DateTime.MinValue;

                    var rows = orderEvents.Query(e =>
                            (accountId == null || e.AccountId == accountId) &&
                            (tokenId == null || e.TokenId == tokenId) &&
                            (signalId == null || e.SignalId == signalId) &&
                            e.Timestamp >= since, limit)
                        .Select(e => new
                        {
                            e.SignalId, ts = e.Timestamp, module = e.ModuleName, e.AccountId, e.TokenId,
                            eventType = e.EventType.ToString(), e.Side, e.Price, e.AmountUsd, e.OrderType,
                            e.Response, details = e.DetailsJson
                        });
                    return JsonSerializer.Serialize(rows, JsonOpts);
                }));

            reg.Register(new SupervisorTool(
                "query_trades",
                "Fragt abgeschlossene Trades aus dem modulübergreifenden Trade-Log ab. " +
                "Filter optional: accountId, moduleName, sinceDays, onlyLosers (true = nur Verlierer).",
                """{"type":"object","properties":{"accountId":{"type":"integer"},"moduleName":{"type":"string"},"sinceDays":{"type":"integer"},"onlyLosers":{"type":"boolean"},"limit":{"type":"integer"}}}""",
                args =>
                {
                    int limit = Math.Clamp(SupervisorToolRegistry.GetInt(args, "limit") ?? 100, 1, 500);
                    int? accountId = SupervisorToolRegistry.GetInt(args, "accountId");
                    string? module = SupervisorToolRegistry.GetString(args, "moduleName");
                    int? sinceDays = SupervisorToolRegistry.GetInt(args, "sinceDays");
                    bool onlyLosers = args.ValueKind == JsonValueKind.Object &&
                                      args.TryGetProperty("onlyLosers", out var ol) && ol.ValueKind == JsonValueKind.True;
                    DateTime since = sinceDays.HasValue ? DateTime.UtcNow.AddDays(-sinceDays.Value) : DateTime.MinValue;

                    var rows = tradeLog.Find(t =>
                            (accountId == null || t.AccountId == accountId) &&
                            (module == null || t.ModuleName == module) &&
                            t.ClosedAt >= since)
                        .Where(t => !onlyLosers || t.RealizedPnl < 0)
                        .OrderByDescending(t => t.ClosedAt)
                        .Take(limit)
                        .Select(t => new
                        {
                            t.SignalId, t.ModuleName, t.AccountId, t.IsDemo, t.TokenId, market = t.MarketQuestion,
                            t.Outcome, t.Side, t.EntryPrice, t.ExitPrice, t.Size, t.RealizedPnl, t.PnlPercent,
                            t.OpenedAt, t.ClosedAt, t.ExitReason
                        });
                    return JsonSerializer.Serialize(rows, JsonOpts);
                }));

            reg.Register(new SupervisorTool(
                "get_dossier",
                "Liefert das komplette Dossier zu einer SignalId als Markdown: Entscheidungskette, Order-Events, Trades, Log-Auszug.",
                """{"type":"object","properties":{"signalId":{"type":"string"}},"required":["signalId"]}""",
                args =>
                {
                    string? signalId = SupervisorToolRegistry.GetString(args, "signalId");
                    if (string.IsNullOrWhiteSpace(signalId)) return "FEHLER: signalId fehlt.";
                    return DossierBuilder.ToMarkdown(dossiers.BuildForSignal(signalId));
                }));

            reg.Register(new SupervisorTool(
                "read_logs",
                "Liest die JSONL-Logdatei eines Tages (Datum yyyy-MM-dd), optional gefiltert nach level, cid (SignalId) und textFilter.",
                """{"type":"object","properties":{"date":{"type":"string"},"level":{"type":"string"},"cid":{"type":"string"},"textFilter":{"type":"string"},"limit":{"type":"integer"}},"required":["date"]}""",
                args =>
                {
                    string? date = SupervisorToolRegistry.GetString(args, "date");
                    if (string.IsNullOrWhiteSpace(date)) return "FEHLER: date fehlt (yyyy-MM-dd).";
                    string path = Path.Combine(logsDir, $"{date}.jsonl");
                    if (!File.Exists(path)) return $"Keine JSONL-Datei für {date}.";

                    string? level = SupervisorToolRegistry.GetString(args, "level");
                    string? cid = SupervisorToolRegistry.GetString(args, "cid");
                    string? text = SupervisorToolRegistry.GetString(args, "textFilter");
                    int limit = Math.Clamp(SupervisorToolRegistry.GetInt(args, "limit") ?? 200, 1, 1000);

                    var lines = new List<LogJson.ParsedLogLine>();
                    foreach (var line in File.ReadLines(path))
                    {
                        var p = LogJson.ParseLine(line);
                        if (p == null) continue;
                        if (level != null && !string.Equals(p.Level, level, StringComparison.OrdinalIgnoreCase)) continue;
                        if (cid != null && p.Cid != cid) continue;
                        if (text != null && !p.Message.Contains(text, StringComparison.OrdinalIgnoreCase)) continue;
                        lines.Add(p);
                        if (lines.Count >= limit) break;
                    }
                    return JsonSerializer.Serialize(lines, JsonOpts);
                }));

            reg.Register(new SupervisorTool(
                "get_kpis",
                "Berechnet Kennzahlen (Netto-PnL, Winrate, Ø-PnL, Profit-Faktor, Trade-Anzahl) über das Trade-Log. " +
                "Filter optional: accountId, moduleName, sinceDays, isDemo.",
                """{"type":"object","properties":{"accountId":{"type":"integer"},"moduleName":{"type":"string"},"sinceDays":{"type":"integer"},"isDemo":{"type":"boolean"}}}""",
                args =>
                {
                    int? accountId = SupervisorToolRegistry.GetInt(args, "accountId");
                    string? module = SupervisorToolRegistry.GetString(args, "moduleName");
                    int? sinceDays = SupervisorToolRegistry.GetInt(args, "sinceDays");
                    bool? isDemo = args.ValueKind == JsonValueKind.Object && args.TryGetProperty("isDemo", out var d) &&
                                   (d.ValueKind == JsonValueKind.True || d.ValueKind == JsonValueKind.False)
                        ? d.GetBoolean() : (bool?)null;
                    DateTime since = sinceDays.HasValue ? DateTime.UtcNow.AddDays(-sinceDays.Value) : DateTime.MinValue;

                    var trades = tradeLog.Find(t =>
                        (accountId == null || t.AccountId == accountId) &&
                        (module == null || t.ModuleName == module) &&
                        (isDemo == null || t.IsDemo == isDemo) &&
                        t.ClosedAt >= since);
                    var k = TradeAnalytics.ComputeKpis(trades);
                    var byModule = TradeAnalytics.PnlByKey(trades, t => t.ModuleName);
                    return JsonSerializer.Serialize(new
                    {
                        k.TradeCount, k.NetPnl, k.WinRatePct, k.AvgPnlPerTrade, k.ProfitFactor,
                        byModule = byModule.Select(x => new { module = x.Key, x.Pnl, x.Count })
                    }, JsonOpts);
                }));

            reg.Register(new SupervisorTool(
                "get_architecture_context",
                "Liefert das kuratierte Architektur-/Verhaltensdokument von PolyTrader (wie die Software entscheidet und handelt).",
                """{"type":"object","properties":{}}""",
                _ => ArchitectureContext.Load()));

            if (counterfactuals != null)
            {
                reg.Register(new SupervisorTool(
                    "query_counterfactuals",
                    "Was wäre aus ABGELEHNTEN BUY-Signalen geworden? Liefert nach Marktauflösung ausgewertete Rejects " +
                    "(Reason, Signalpreis, Gewinner ja/nein, hypothetischer PnL je Share) — zeigt, ob Risk-Limits Gewinne oder Verluste verhindert haben.",
                    """{"type":"object","properties":{"limit":{"type":"integer"}}}""",
                    args =>
                    {
                        int limit = Math.Clamp(SupervisorToolRegistry.GetInt(args, "limit") ?? 200, 1, 500);
                        var rows = counterfactuals.GetRecent(limit).Select(c => new
                        {
                            c.SignalId, c.CheckedAt, c.ModuleName, c.AccountId, market = c.MarketQuestion,
                            reason = c.Reason, price = c.SignalPrice, c.IsWinner, pnlPerShare = c.HypotheticalPnlPerShare
                        });
                        return JsonSerializer.Serialize(rows, JsonOpts);
                    }));
            }

            return reg;
        }
    }
}
