using System.Collections.Generic;
using System.Linq;
using PolyTrader.Modules.ResolutionFarming.Logic;
using PolyTrader.Modules.ResolutionFarming.Models;
using Xunit;

namespace PolyTrader.Tests
{
    /// <summary>Sicherheitsnetz für die reine Entry-Planung (Score-Priorisierung unter allen Risiko-Limits).</summary>
    public class FarmingExecutionPlannerTests
    {
        private static RfSettings Settings(decimal maxPerMarket = 25m, decimal clusterPct = 10m,
            decimal totalPct = 60m, int maxPerDay = 20, decimal killUsd = 0m) => new()
        {
            MaxPerMarketUsd = maxPerMarket, MaxPerClusterPct = clusterPct, MaxTotalExposurePct = totalPct,
            MaxNewPositionsPerDay = maxPerDay, DailyLossKillSwitchUsd = killUsd
        };

        private static RfCandidate Cand(string token, decimal score, string cluster = "c", decimal ask = 0.95m) =>
            new() { Accepted = true, TokenId = token, Score = score, ClusterKey = cluster, Ask = ask };

        [Fact]
        public void Plan_ranks_by_score_and_sizes_to_limits()
        {
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 3m, "c1"), Cand("b", 5m, "c2") },
                new List<RfPosition>(), Settings(), bankrollUsd: 1000m, newPositionsToday: 0, realizedDailyPnlUsd: 0m);

            Assert.Equal(2, plan.Count);
            Assert.Equal("b", plan[0].candidate.TokenId);   // höherer Score zuerst
            Assert.Equal(25m, plan[0].sizeUsd);             // min(Markt 25, Cluster 100, Gesamt 600)
        }

        [Fact]
        public void Plan_skips_already_held_tokens()
        {
            var open = new List<RfPosition> { new() { TokenId = "a", ClusterKey = "c1", AmountUsd = 10m } };
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 9m, "c1") }, open, Settings(), 1000m, 0, 0m);

            Assert.Empty(plan);
        }

        [Fact]
        public void Plan_cluster_limit_caps_second_open_in_same_cluster()
        {
            // Cluster 3% von 1000 = 30. Erste 25, zweite nur noch 5.
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m, "cx"), Cand("b", 4m, "cx") },
                new List<RfPosition>(), Settings(clusterPct: 3m), 1000m, 0, 0m);

            Assert.Equal(2, plan.Count);
            Assert.Equal(25m, plan[0].sizeUsd);
            Assert.Equal(5m, plan[1].sizeUsd);
        }

        [Fact]
        public void Plan_total_exposure_limit_caps()
        {
            // Gesamt 3% von 1000 = 30. Erste 25, zweite 5.
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m, "c1"), Cand("b", 4m, "c2") },
                new List<RfPosition>(), Settings(totalPct: 3m), 1000m, 0, 0m);

            Assert.Equal(25m, plan[0].sizeUsd);
            Assert.Equal(5m, plan[1].sizeUsd);
        }

        [Fact]
        public void Plan_kill_switch_blocks_all()
        {
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m) }, new List<RfPosition>(),
                Settings(killUsd: 10m), 1000m, 0, realizedDailyPnlUsd: -10m);

            Assert.Empty(plan);
        }

        [Fact]
        public void Plan_daily_limit_stops_new_opens()
        {
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m, "c1"), Cand("b", 4m, "c2") },
                new List<RfPosition>(), Settings(maxPerDay: 1), 1000m, newPositionsToday: 0, realizedDailyPnlUsd: 0m);

            Assert.Single(plan);
        }

        [Fact]
        public void Plan_dedupes_duplicate_candidate_tokens()
        {
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m, "c1"), Cand("a", 3m, "c1") },
                new List<RfPosition>(), Settings(), 1000m, 0, 0m);

            Assert.Single(plan);
            Assert.Equal(5m, plan[0].candidate.Score);   // höherer Score gewinnt
        }

        [Fact]
        public void Plan_empty_when_bankroll_zero()
        {
            var plan = FarmingExecutionPlanner.Plan(
                new[] { Cand("a", 5m) }, new List<RfPosition>(), Settings(), bankrollUsd: 0m, 0, 0m);
            Assert.Empty(plan);
        }
    }
}
