using System;
using System.Diagnostics;
using System.Threading;

namespace PolyTrader.Core.Agents
{
    /// <summary>Welche Budget-Dimension überschritten wurde.</summary>
    public enum AgentBudgetKind
    {
        Steps,
        Tokens,
        Time
    }

    /// <summary>Wird geworfen, sobald eine Budget-Dimension eines Agenten-Laufs überschritten ist.</summary>
    public sealed class AgentBudgetExceededException : Exception
    {
        public AgentBudgetKind Kind { get; }

        public AgentBudgetExceededException(AgentBudgetKind kind, string message) : base(message)
        {
            Kind = kind;
        }
    }

    /// <summary>
    /// Erzwingt ein <see cref="AgentBudget"/> für EINEN Agenten-Lauf (nicht wiederverwenden – pro Lauf neu
    /// instanziieren). Aus ClawdDotNet übernommen und um ein Zeitbudget ergänzt. Thread-safe (Interlocked),
    /// damit auch parallele Tool-Aufrufe sauber gezählt werden.
    /// </summary>
    public sealed class LoopGuard
    {
        private readonly AgentBudget _budget;
        private readonly Stopwatch _clock = Stopwatch.StartNew();
        private int _steps;
        private int _tokens;

        public LoopGuard(AgentBudget budget) => _budget = budget ?? AgentBudget.Default;

        public int Steps => Volatile.Read(ref _steps);
        public int Tokens => Volatile.Read(ref _tokens);
        public TimeSpan Elapsed => _clock.Elapsed;

        /// <summary>Zählt eine Modell-Runde. Wirft bei Überschreitung der Schrittzahl oder des Zeitbudgets.</summary>
        public void RecordStep()
        {
            if (Interlocked.Increment(ref _steps) > _budget.MaxSteps)
                throw new AgentBudgetExceededException(AgentBudgetKind.Steps,
                    $"Maximale Schrittzahl ({_budget.MaxSteps}) überschritten.");
            ThrowIfExpired();
        }

        /// <summary>Addiert verbrauchte Tokens. Wirft bei Überschreitung des Token-Budgets.</summary>
        public void RecordTokens(int count)
        {
            if (count <= 0) return;
            int total = Interlocked.Add(ref _tokens, count);
            if (total > _budget.MaxTokens)
                throw new AgentBudgetExceededException(AgentBudgetKind.Tokens,
                    $"Maximales Token-Budget ({_budget.MaxTokens}) überschritten (verbraucht: {total}).");
        }

        /// <summary>Wirft, wenn das Zeitbudget abgelaufen ist.</summary>
        public void ThrowIfExpired()
        {
            if (_clock.Elapsed > _budget.Timeout)
                throw new AgentBudgetExceededException(AgentBudgetKind.Time,
                    $"Zeitbudget ({_budget.Timeout.TotalSeconds:0}s) überschritten.");
        }
    }
}
