using System.Collections.Generic;
using System.Linq;
using Predictalytics.Application.DTOs;
using Predictalytics.Application.Services;
using Xunit;

namespace Predictalytics.Application.Tests.Services;

public class ShowcaseBuilderTests
{
    private static TraderDto Dto(int id) => new(
        id, "Polymarket", "0x" + id, "T" + id, "Bronze", "Unknown",
        CombinedScore: 0, CopytradingScore: 0, CopytradingQualityScore: 0, CopytradingCopyabilityScore: 0,
        WinRate: 0, TotalPnl: 0, TotalTrades: 0, Trades30d: 0, PnL30d: 0, WinRate30d: 0, EstimatedBankroll: 0,
        IsOnWatchlist: false, IsSuspectedBot: false, LastPolledAt: null, Traits: null);

    private static ShowcaseCandidate Cand(
        int id,
        decimal copytrading = 0, decimal totalPnl = 0, decimal pnl30d = 0,
        decimal? profitFactor = null, decimal? calmar = null, decimal maxDd = 0,
        decimal? conviction = null, decimal concentration = 0,
        bool bot = false, bool snapshot = false, int totalTrades = 0,
        bool insider = false, bool farmer = false)
        => new(Dto(id), copytrading, totalPnl, pnl30d, profitFactor, calmar, maxDd,
               conviction, concentration, bot, snapshot, totalTrades, insider, farmer);

    private static List<int> Ids(IReadOnlyList<ShowcaseSection> s, string key)
        => s.FirstOrDefault(x => x.Key == key)?.Traders.Select(t => t.Id).ToList() ?? new List<int>();

    [Fact]
    public void CopyReady_IncludesQualified_ExcludesBotAndThinProfit()
    {
        var s = ShowcaseBuilder.Build(new[]
        {
            Cand(1, copytrading: 70, totalTrades: 50, profitFactor: 2.0m),   // qualifies
            Cand(2, copytrading: 70, totalTrades: 50, profitFactor: 2.0m, bot: true), // bot -> out
            Cand(3, copytrading: 70, totalTrades: 50, profitFactor: 1.1m),   // thin profit -> out
            Cand(4, copytrading: 40, totalTrades: 50, profitFactor: 2.0m),   // low score -> out
        });

        var ids = Ids(s, "copy_ready");
        Assert.Contains(1, ids);
        Assert.DoesNotContain(2, ids);
        Assert.DoesNotContain(3, ids);
        Assert.DoesNotContain(4, ids);
    }

    [Fact]
    public void SmoothOperators_RanksByCalmar_RequiresDrawdown()
    {
        var s = ShowcaseBuilder.Build(new[]
        {
            Cand(1, totalPnl: 500, calmar: 5m, maxDd: 100m),
            Cand(2, totalPnl: 500, calmar: 9m, maxDd: 50m),
            Cand(3, totalPnl: 500, calmar: null, maxDd: 0m), // no drawdown recorded -> excluded
        });

        var ids = Ids(s, "smooth_operators");
        Assert.Equal(new[] { 2, 1 }, ids);   // higher calmar first
        Assert.DoesNotContain(3, ids);
    }

    [Fact]
    public void HighConviction_OnlyPositive()
    {
        var s = ShowcaseBuilder.Build(new[]
        {
            Cand(1, conviction: 25m),
            Cand(2, conviction: -10m),   // overbets losers -> out of conviction
            Cand(3, conviction: null),
        });

        Assert.Equal(new[] { 1 }, Ids(s, "high_conviction"));
    }

    [Fact]
    public void RedFlags_FlagsHighPnlWithNegativeConvictionOrFarming()
    {
        var s = ShowcaseBuilder.Build(new[]
        {
            Cand(1, totalPnl: 10000, conviction: -5m),                 // negative conviction
            Cand(2, totalPnl: 20000, profitFactor: 1.05m),             // thin profit factor
            Cand(3, totalPnl: 30000, farmer: true),                    // resolution farmer
            Cand(4, totalPnl: 50000, conviction: 10m, profitFactor: 3m), // clean whale -> not flagged
        });

        var ids = Ids(s, "red_flags");
        Assert.Contains(1, ids);
        Assert.Contains(2, ids);
        Assert.Contains(3, ids);
        Assert.DoesNotContain(4, ids);
    }

    [Fact]
    public void InsiderWatch_OnlyPossibleInsiders()
    {
        var s = ShowcaseBuilder.Build(new[]
        {
            Cand(1, totalPnl: 5000, insider: true),
            Cand(2, totalPnl: 9000, insider: false),
        });

        Assert.Equal(new[] { 1 }, Ids(s, "insider_watch"));
    }

    [Fact]
    public void EmptySections_AreOmitted()
    {
        // No candidate qualifies for anything -> no sections at all.
        var s = ShowcaseBuilder.Build(new[] { Cand(1) });
        Assert.Empty(s);
    }
}
