using System;
using System.Threading;
using PolyTrader.Core.Agents;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>
    /// Sicherheitsnetz für die aus ClawdDotNet übernommenen Agenten-Bausteine: LoopGuard (Kostendeckel
    /// Schritte/Tokens/Zeit) und PermissionGate (Tool-Allow-List).
    /// </summary>
    public class AgentGuardTests
    {
        // ----- LoopGuard -----

        [Fact]
        public void LoopGuard_throws_when_step_limit_exceeded()
        {
            var guard = new LoopGuard(new AgentBudget { MaxSteps = 2, MaxTokens = 1_000_000, Timeout = TimeSpan.FromMinutes(5) });

            guard.RecordStep();   // 1
            guard.RecordStep();   // 2
            var ex = Assert.Throws<AgentBudgetExceededException>(() => guard.RecordStep());   // 3 -> Abbruch
            Assert.Equal(AgentBudgetKind.Steps, ex.Kind);
        }

        [Fact]
        public void LoopGuard_throws_when_token_budget_exceeded()
        {
            var guard = new LoopGuard(new AgentBudget { MaxSteps = 100, MaxTokens = 100, Timeout = TimeSpan.FromMinutes(5) });

            guard.RecordTokens(60);
            var ex = Assert.Throws<AgentBudgetExceededException>(() => guard.RecordTokens(60));   // 120 > 100
            Assert.Equal(AgentBudgetKind.Tokens, ex.Kind);
            Assert.Equal(120, guard.Tokens);
        }

        [Fact]
        public void LoopGuard_ignores_non_positive_token_counts()
        {
            var guard = new LoopGuard(new AgentBudget { MaxTokens = 10 });
            guard.RecordTokens(0);
            guard.RecordTokens(-5);
            Assert.Equal(0, guard.Tokens);
        }

        [Fact]
        public void LoopGuard_throws_when_time_budget_exceeded()
        {
            var guard = new LoopGuard(new AgentBudget { MaxSteps = 100, MaxTokens = 1_000_000, Timeout = TimeSpan.FromMilliseconds(10) });

            Thread.Sleep(40);
            var ex = Assert.Throws<AgentBudgetExceededException>(() => guard.ThrowIfExpired());
            Assert.Equal(AgentBudgetKind.Time, ex.Kind);
        }

        // ----- PermissionGate -----

        [Fact]
        public void PermissionGate_allows_listed_tool_case_insensitive()
        {
            var gate = new PermissionGate();
            var allowed = new[] { "read_logs", "query_trades" };

            Assert.True(gate.IsAllowed("READ_LOGS", allowed));
            Assert.False(gate.IsAllowed("place_order", allowed));
        }

        [Fact]
        public void PermissionGate_null_allowlist_permits_everything()
        {
            var gate = new PermissionGate();
            Assert.True(gate.IsAllowed("anything", null));
        }

        [Fact]
        public void PermissionGate_enforce_throws_for_denied_tool()
        {
            var gate = new PermissionGate();
            Assert.Throws<ToolAccessDeniedException>(() => gate.Enforce("place_order", new[] { "read_logs" }));
        }
    }
}
