using SportsDivision.Application.Services;

namespace SportsDivision.Application.Tests;

public class ScoringServiceTests
{
    private readonly ScoringService _service;

    public ScoringServiceTests()
    {
        // ScoringService is needed; CalculatePoints is a pure method so we can pass nulls for unused deps
        _service = new ScoringService(null!, null!);
    }

    // World Athletics scoring formula tests
    // Track: Points = A * (B - T)^C  where T = time in seconds
    // Field/Jump: Points = A * (P - B)^C  where P = performance (distance/height)

    [Fact]
    public void CalculatePoints_Track_BasicCalculation()
    {
        // Example: A=24.63, B=18.0, C=1.81, T=10.0 (100m time)
        // Points = 24.63 * (18.0 - 10.0)^1.81
        decimal a = 24.63m;
        decimal b = 18.0m;
        decimal c = 1.81m;
        decimal time = 10.0m;

        var points = _service.CalculatePoints(time, a, b, c, isTrack: true);

        // (18.0 - 10.0) = 8.0
        // 8.0^1.81 ≈ 49.33
        // 24.63 * 49.33 ≈ 1215
        Assert.True(points > 0);
        Assert.True(points > 1000, $"Expected > 1000 for elite 100m time, got {points}");
    }

    [Fact]
    public void CalculatePoints_Track_SlowerTimeLowerPoints()
    {
        decimal a = 24.63m;
        decimal b = 18.0m;
        decimal c = 1.81m;

        var fastPoints = _service.CalculatePoints(10.0m, a, b, c, isTrack: true);
        var slowPoints = _service.CalculatePoints(12.0m, a, b, c, isTrack: true);

        Assert.True(fastPoints > slowPoints,
            $"Fast time ({fastPoints}) should score more than slow time ({slowPoints})");
    }

    [Fact]
    public void CalculatePoints_Track_TimeEqualsB_ReturnsZero()
    {
        // When T = B, diff = 0, points should be 0
        decimal a = 24.63m;
        decimal b = 18.0m;
        decimal c = 1.81m;

        var points = _service.CalculatePoints(18.0m, a, b, c, isTrack: true);
        Assert.Equal(0, points);
    }

    [Fact]
    public void CalculatePoints_Track_TimeExceedsB_ReturnsZero()
    {
        // When T > B, diff is negative, should return 0
        decimal a = 24.63m;
        decimal b = 18.0m;
        decimal c = 1.81m;

        var points = _service.CalculatePoints(20.0m, a, b, c, isTrack: true);
        Assert.Equal(0, points);
    }

    [Fact]
    public void CalculatePoints_Field_BasicCalculation()
    {
        // Field: Points = A * (P - B)^C
        // Example: A=0.14354, B=220, C=1.4, P=800 (shot put in cm)
        decimal a = 0.14354m;
        decimal b = 220m;
        decimal c = 1.4m;
        decimal performance = 800m;

        var points = _service.CalculatePoints(performance, a, b, c, isTrack: false);

        Assert.True(points > 0);
    }

    [Fact]
    public void CalculatePoints_Field_HigherPerformanceMorePoints()
    {
        decimal a = 0.14354m;
        decimal b = 220m;
        decimal c = 1.4m;

        var highPoints = _service.CalculatePoints(800m, a, b, c, isTrack: false);
        var lowPoints = _service.CalculatePoints(600m, a, b, c, isTrack: false);

        Assert.True(highPoints > lowPoints,
            $"Higher performance ({highPoints}) should score more than lower ({lowPoints})");
    }

    [Fact]
    public void CalculatePoints_Field_PerformanceEqualsB_ReturnsZero()
    {
        decimal a = 0.14354m;
        decimal b = 220m;
        decimal c = 1.4m;

        var points = _service.CalculatePoints(220m, a, b, c, isTrack: false);
        Assert.Equal(0, points);
    }

    [Fact]
    public void CalculatePoints_Field_PerformanceBelowB_ReturnsZero()
    {
        decimal a = 0.14354m;
        decimal b = 220m;
        decimal c = 1.4m;

        var points = _service.CalculatePoints(100m, a, b, c, isTrack: false);
        Assert.Equal(0, points);
    }

    [Fact]
    public void CalculatePoints_FloorsTruncatesDecimal()
    {
        // Use values that would produce a fractional result
        decimal a = 1.0m;
        decimal b = 10.0m;
        decimal c = 1.0m;
        decimal performance = 5.0m;

        // Track: Points = 1.0 * (10.0 - 5.0)^1.0 = 5.0 → Floor = 5
        var points = _service.CalculatePoints(performance, a, b, c, isTrack: true);
        Assert.Equal(5, points);
    }

    [Fact]
    public void CalculatePoints_Track_KnownWorldAthleticsValues()
    {
        // Men's 100m: A=25.4347, B=18, C=1.81
        // 10.00s should give ~1096 points
        decimal a = 25.4347m;
        decimal b = 18.0m;
        decimal c = 1.81m;

        var points = _service.CalculatePoints(10.0m, a, b, c, isTrack: true);

        // Expected approximately 1096 for 10.00s 100m
        Assert.InRange(points, 1050, 1150);
    }
}
