using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Engine;
using Shouldly;

namespace ClawdDotNet.Core.Tests.Engine;

public sealed class LoopGuardTests
{
    // ═══════════════════════════════════════════════════════════
    //  L1 — Schrittzählung
    // ═══════════════════════════════════════════════════════════

    [Fact]
    public void RecordStep_erlaubt_genau_MaxSteps_Schritte()
    {
        var guard = new LoopGuard(new LoopGuardConfig { MaxSteps = 5 });

        for (var i = 0; i < 5; i++)
            guard.RecordStep();

        guard.Steps.ShouldBe(5);
        Should.Throw<LoopLimitExceededException>(() => guard.RecordStep());
    }

    [Fact]
    public void RecordStep_nennt_das_erreichte_Limit_in_der_Meldung()
    {
        var guard = new LoopGuard(new LoopGuardConfig { MaxSteps = 3 });
        for (var i = 0; i < 3; i++) guard.RecordStep();

        var ex = Should.Throw<LoopLimitExceededException>(() => guard.RecordStep());
        ex.Message.ShouldContain("3");
    }

    // ═══════════════════════════════════════════════════════════
    //  L2 — Die maxTokens-Semantik (Bug B3)
    // ═══════════════════════════════════════════════════════════

    /// <summary>
    /// Kernproblem B3: RecordTokens summiert die TotalTokens jedes Schritts.
    /// Da jeder Schritt den kompletten Kontext erneut sendet, wächst diese Summe
    /// quadratisch — ein völlig normaler Chat bricht dadurch nach wenigen Schritten ab.
    ///
    /// Dieser Test beschreibt das GEWÜNSCHTE Verhalten: Ein Agent mit einem stabilen
    /// 20k-Kontext muss 10 Schritte durchhalten können.
    ///
    /// Seit der Trennung von maxCumulativeTokens (Kostenbudget) und
    /// maxContextTokens (Kontextgröße) läuft er durch.
    /// </summary>
    [Fact]
    public void Ein_stabiler_Kontext_ueberlebt_zehn_Schritte()
    {
        var config = new LoopGuardConfig();          // bewusst die Produktiv-Defaults
        var guard = new LoopGuard(config);

        Should.NotThrow(() =>
        {
            for (var step = 0; step < 10; step++)
            {
                guard.RecordStep();
                // Realistisch: 20k Kontext geht rein, ~500 Tokens kommen raus.
                guard.RecordTokens(20_000 + 500);
            }
        }, $"Ein Kontext von 20k über 10 Schritte ist normal und darf nicht am " +
           $"Kostenbudget {config.MaxCumulativeTokens} scheitern.");
    }

    [Fact]
    public void RecordTokens_greift_wenn_das_Kostenbudget_wirklich_erschoepft_ist()
    {
        var guard = new LoopGuard(new LoopGuardConfig { MaxCumulativeTokens = 1_000 });

        Should.Throw<LoopLimitExceededException>(() => guard.RecordTokens(1_001));
    }

    [Fact]
    public void Die_Meldung_unterscheidet_Schritt_und_Kostenlimit()
    {
        var stepGuard = new LoopGuard(new LoopGuardConfig { MaxSteps = 1 });
        stepGuard.RecordStep();
        Should.Throw<LoopLimitExceededException>(() => stepGuard.RecordStep())
            .Message.ShouldContain("Schritt-Limit");

        var tokenGuard = new LoopGuard(new LoopGuardConfig { MaxCumulativeTokens = 100 });
        Should.Throw<LoopLimitExceededException>(() => tokenGuard.RecordTokens(101))
            .Message.ShouldContain("Kostenbudget");
    }

    // ═══════════════════════════════════════════════════════════
    //  L3 — Thread-Sicherheit
    // ═══════════════════════════════════════════════════════════

    [Fact]
    public void RecordStep_zaehlt_unter_Parallelzugriff_korrekt()
    {
        var guard = new LoopGuard(new LoopGuardConfig { MaxSteps = int.MaxValue });

        Parallel.For(0, 1_000, _ => guard.RecordStep());

        guard.Steps.ShouldBe(1_000);
    }

    [Fact]
    public void RecordTokens_summiert_unter_Parallelzugriff_korrekt()
    {
        var guard = new LoopGuard(new LoopGuardConfig { MaxCumulativeTokens = int.MaxValue });

        Parallel.For(0, 1_000, _ => guard.RecordTokens(10));

        guard.Tokens.ShouldBe(10_000);
    }
}
