using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using PolyTrader.Core.Agents;

namespace PolyTrader.Modules.Supervisor.Agent
{
    /// <summary>Ergebnis einer Agenten-Anfrage inkl. transparenter Tool-Aufruf-Historie.</summary>
    public sealed class AgentResult
    {
        public string Answer { get; init; } = string.Empty;
        public List<(string Tool, string Arguments, string Result)> ToolInvocations { get; init; } = new();
        public int PromptTokens { get; init; }
        public int CompletionTokens { get; init; }
    }

    /// <summary>
    /// Der Analyse-Agent (S-2): Function-Calling-Loop gegen einen <see cref="IChatCompletionClient"/>
    /// mit der read-only <see cref="SupervisorToolRegistry"/>. System-Kontext = Arbeitsanweisung +
    /// Architektur-Dokument. Harte Iterationsgrenze gegen Endlosschleifen; jeder Tool-Aufruf wird
    /// festgehalten (Nachvollziehbarkeit in der UI).
    /// </summary>
    public sealed class SupervisorAgent
    {
        /// <summary>Default-Schrittzahl eines Laufs (deckungsgleich mit <see cref="AgentBudget.MaxSteps"/>).</summary>
        public const int MaxIterations = 8;
        public const string DefaultModel = "openrouter/auto";

        private readonly IChatCompletionClient _chat;
        private readonly SupervisorToolRegistry _tools;
        private readonly PermissionGate _gate = new();

        public SupervisorAgent(IChatCompletionClient chat, SupervisorToolRegistry tools)
        {
            _chat = chat;
            _tools = tools;
        }

        private static string SystemPrompt(SupervisorProfile profile)
        {
            string basePrompt =
                "Du bist der Supervisor von PolyTrader: ein Analyse-Agent für automatisierten Polymarket-Handel. " +
                "Du bist strikt read-only – du kannst und darfst nicht handeln. Nutze die Tools, um Entscheidungsjournal, " +
                "Order-Events, Trades und Logs abzufragen, BEVOR du Schlüsse ziehst. Zitiere konkrete Daten " +
                "(SignalIds, Zeiten, Preise, ReasonCodes). Antworte auf Deutsch, präzise und mit klarer Schlussfolgerung.";
            if (!string.IsNullOrEmpty(profile.PromptAddendum))
                basePrompt += "\n\n" + profile.PromptAddendum;
            return basePrompt + "\n\n=== ARCHITEKTUR-KONTEXT ===\n" + ArchitectureContext.Load();
        }

        /// <summary>Tools des Profils (Subset oder alle).</summary>
        private IReadOnlyList<SupervisorTool> ToolsFor(SupervisorProfile profile) =>
            profile.ToolFilter == null
                ? _tools.Tools
                : _tools.Tools.Where(t => Array.Exists(profile.ToolFilter, n =>
                    string.Equals(n, t.Name, StringComparison.OrdinalIgnoreCase))).ToList();

        /// <summary>
        /// Beantwortet eine Analyse-Frage. <paramref name="progress"/> meldet Tool-Aufrufe live an die UI.
        /// </summary>
        public async Task<AgentResult> AskAsync(string question, string? model = null,
            IProgress<string>? progress = null, SupervisorProfile? profile = null, CancellationToken ct = default)
        {
            var activeProfile = profile ?? SupervisorProfiles.Allgemein;
            var activeTools = ToolsFor(activeProfile);
            var allowedToolNames = activeTools.Select(t => t.Name).ToList();   // Allow-List = angebotene Tools
            var messages = new List<ChatMessage>
            {
                ChatMessage.System(SystemPrompt(activeProfile)),
                ChatMessage.User(question)
            };
            var invocations = new List<(string, string, string)>();
            int promptTokens = 0, completionTokens = 0;
            string usedModel = string.IsNullOrWhiteSpace(model) ? DefaultModel : model.Trim();

            // Kostendeckel des Laufs: Schritte/Tokens/Zeit (Default-Schritte = MaxIterations, Rückwärtskompatibel).
            var budget = activeProfile.Budget ?? new AgentBudget { MaxSteps = MaxIterations };
            var guard = new LoopGuard(budget);

            while (true)
            {
                ct.ThrowIfCancellationRequested();

                try { guard.RecordStep(); }
                catch (AgentBudgetExceededException ex) { return Abort(ex); }

                var response = await _chat.CompleteAsync(usedModel, messages, activeTools, ct);
                promptTokens += response.PromptTokens;
                completionTokens += response.CompletionTokens;
                try { guard.RecordTokens(response.PromptTokens + response.CompletionTokens); }
                catch (AgentBudgetExceededException ex) { return Abort(ex); }

                if (response.ToolCalls.Count == 0)
                {
                    return new AgentResult
                    {
                        Answer = response.Content ?? "(keine Antwort)",
                        ToolInvocations = invocations,
                        PromptTokens = promptTokens,
                        CompletionTokens = completionTokens
                    };
                }

                messages.Add(ChatMessage.Assistant(response.Content, response.ToolCalls));
                foreach (var call in response.ToolCalls)
                {
                    progress?.Report($"🔧 {call.Name}({call.ArgumentsJson})");
                    // Gate härtet gegen Modell-Fehlgriffe: nicht freigegebene Tools werden NICHT ausgeführt,
                    // sondern als Fehlertext ans Modell zurückgegeben (fehlertolerant, wie die Registry selbst).
                    string result = _gate.IsAllowed(call.Name, allowedToolNames)
                        ? _tools.Execute(call.Name, call.ArgumentsJson)
                        : $"FEHLER: Tool '{call.Name}' ist für dieses Profil nicht freigegeben.";
                    invocations.Add((call.Name, call.ArgumentsJson, result));
                    messages.Add(ChatMessage.ToolResult(call.Id, result));
                }
            }

            // Kontrollierter Abbruch bei erschöpftem Budget – gibt bisherige Ergebnisse transparent zurück.
            AgentResult Abort(AgentBudgetExceededException ex)
            {
                string reason = ex.Kind switch
                {
                    AgentBudgetKind.Steps => "maximale Tool-Iterationen erreicht",
                    AgentBudgetKind.Tokens => "Token-Budget erschöpft",
                    AgentBudgetKind.Time => "Zeitbudget erschöpft",
                    _ => ex.Message
                };
                return new AgentResult
                {
                    Answer = $"Abbruch: {reason} (Frage ggf. eingrenzen).",
                    ToolInvocations = invocations,
                    PromptTokens = promptTokens,
                    CompletionTokens = completionTokens
                };
            }
        }
    }
}
