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

namespace Predictalytics.Application.Tests.Services;

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

    // Two traders co-entering `count` shared outcomes within the window.
    private static IEnumerable<CandidateEntry> CoEnter(int a, int b, int count, int outcomeBase = 0)
    {
        for (int i = 0; i < count; i++)
        {
            var oid = outcomeBase + i;
            yield return new CandidateEntry(a, oid, T0.AddDays(i));
            yield return new CandidateEntry(b, oid, T0.AddDays(i).AddHours(2));
        }
    }

    [Fact]
    public void EdgeFormsOnlyAtOrAboveMinWeight()
    {
        // 10 & 20 share 4 outcomes; 30 & 40 share only 2 -> below minEdge=3.
        var entries = CoEnter(10, 20, 4, outcomeBase: 0)
            .Concat(CoEnter(30, 40, 2, outcomeBase: 100))
            .ToList();

        var g = CoMovementGraphBuilder.Build(entries, windowHours: 48, minEdgeWeight: 3);

        Assert.Single(g.Edges);
        Assert.Equal(4, g.Edges[0].Weight);
        Assert.Contains(10, g.ClusterByTrader.Keys);
        Assert.DoesNotContain(30, g.ClusterByTrader.Keys); // no qualifying edge -> not in graph
    }

    [Fact]
    public void OutsideWindowDoesNotCount()
    {
        var entries = new List<CandidateEntry>
        {
            new(1, 1, T0), new(2, 1, T0.AddDays(5)),   // 5 days apart -> outside 48h
            new(1, 2, T0), new(2, 2, T0.AddDays(5)),
            new(1, 3, T0), new(2, 3, T0.AddDays(5)),
        };
        var g = CoMovementGraphBuilder.Build(entries, windowHours: 48, minEdgeWeight: 3);
        Assert.Empty(g.Edges);
    }

    [Fact]
    public void ConnectedComponentsFormClusters()
    {
        // 1-2-3 chained into one cluster; 4-5 a separate cluster.
        var entries = CoEnter(1, 2, 3, 0)
            .Concat(CoEnter(2, 3, 3, 50))
            .Concat(CoEnter(4, 5, 3, 100))
            .ToList();

        var g = CoMovementGraphBuilder.Build(entries, minEdgeWeight: 3);

        Assert.Equal(g.ClusterByTrader[1], g.ClusterByTrader[2]);
        Assert.Equal(g.ClusterByTrader[2], g.ClusterByTrader[3]);
        Assert.Equal(g.ClusterByTrader[4], g.ClusterByTrader[5]);
        Assert.NotEqual(g.ClusterByTrader[1], g.ClusterByTrader[4]);
        Assert.Equal(2, g.ClusterByTrader.Values.Distinct().Count());
    }
}
