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

namespace Predictalytics.Application.Tests.Services;

public class CoMovementCalculatorTests
{
    private static readonly DateTime T0 = new(2026, 7, 1, 12, 0, 0, DateTimeKind.Utc);

    [Fact]
    public void RanksCoMovers_AndRequiresMinSharedMarkets()
    {
        // Seed entered outcomes 1,2,3,4.
        var seed = new List<CoEntry>
        {
            new(1, T0), new(2, T0.AddDays(1)), new(3, T0.AddDays(2)), new(4, T0.AddDays(3))
        };
        var cands = new List<CandidateEntry>
        {
            // Trader 10 co-enters 1,2,3 within the window -> qualifies (>=3).
            new(10, 1, T0.AddHours(-2)),
            new(10, 2, T0.AddDays(1).AddHours(-1)),
            new(10, 3, T0.AddDays(2).AddHours(3)),
            // Trader 20 co-enters only 1,2 -> below minShared.
            new(20, 1, T0.AddHours(1)),
            new(20, 2, T0.AddDays(1)),
            // Trader 30 enters outcome 3 but far outside the window -> ignored.
            new(30, 3, T0.AddDays(10)),
        };

        var ranked = CoMovementCalculator.Rank(seed, cands, windowHours: 48, minSharedMarkets: 3);

        Assert.Single(ranked);
        Assert.Equal(10, ranked[0].TraderId);
        Assert.Equal(3, ranked[0].SharedMarkets);
    }

    [Fact]
    public void PositiveLead_MeansCandidateMovedBeforeSeed()
    {
        var seed = new List<CoEntry> { new(1, T0), new(2, T0.AddDays(1)), new(3, T0.AddDays(2)) };
        // Candidate consistently enters 3 hours BEFORE the seed.
        var cands = new List<CandidateEntry>
        {
            new(10, 1, T0.AddHours(-3)),
            new(10, 2, T0.AddDays(1).AddHours(-3)),
            new(10, 3, T0.AddDays(2).AddHours(-3)),
        };

        var ranked = CoMovementCalculator.Rank(seed, cands);

        Assert.Single(ranked);
        Assert.True(ranked[0].AvgLeadHours > 0);
        Assert.Equal(3.0, ranked[0].AvgLeadHours, 1);
    }

    [Fact]
    public void HigherSharedMarketCount_RanksFirst()
    {
        var seed = Enumerable.Range(1, 6).Select(i => new CoEntry(i, T0.AddHours(i))).ToList();
        var cands = new List<CandidateEntry>();
        // Trader 10 shares 5 markets, trader 20 shares 3.
        for (int i = 1; i <= 5; i++) cands.Add(new(10, i, T0.AddHours(i)));
        for (int i = 1; i <= 3; i++) cands.Add(new(20, i, T0.AddHours(i)));

        var ranked = CoMovementCalculator.Rank(seed, cands, minSharedMarkets: 3);

        Assert.Equal(2, ranked.Count);
        Assert.Equal(10, ranked[0].TraderId);
        Assert.Equal(20, ranked[1].TraderId);
    }
}
