using Moq;
using SportsDivision.Application.Services;
using SportsDivision.Domain.Entities;
using SportsDivision.Domain.Enums;
using SportsDivision.Domain.Interfaces;

namespace SportsDivision.Application.Tests;

public class HighJumpEliminationTests
{
    private readonly Mock<IUnitOfWork> _mockUow;
    private readonly HighJumpService _service;

    public HighJumpEliminationTests()
    {
        _mockUow = new Mock<IUnitOfWork>();
        _service = new HighJumpService(_mockUow.Object, null!);
    }

    private HighJumpHeight CreateHeight(int heightId, decimal height, int sortOrder, HighJumpAttempt? attempt = null)
    {
        var h = new HighJumpHeight
        {
            HighJumpHeightId = heightId,
            Height = height,
            SortOrder = sortOrder,
            TournamentEventLevelId = 1,
            Attempts = new List<HighJumpAttempt>()
        };
        if (attempt != null)
        {
            attempt.HighJumpHeightId = heightId;
            h.Attempts.Add(attempt);
        }
        return h;
    }

    private HighJumpAttempt CreateAttempt(int regId, HighJumpAttemptResult? a1, HighJumpAttemptResult? a2 = null, HighJumpAttemptResult? a3 = null)
    {
        return new HighJumpAttempt
        {
            EventRegistrationId = regId,
            Attempt1 = a1,
            Attempt2 = a2,
            Attempt3 = a3
        };
    }

    [Fact]
    public async Task IsEliminated_ThreeConsecutiveFailsAtOneHeight_ReturnsTrue()
    {
        var heights = new List<HighJumpHeight>
        {
            CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Clear)),
            CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail))
        };

        _mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
            .ReturnsAsync(heights);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);

        var result = await _service.IsEliminatedAsync(1, 1);

        Assert.True(result);
    }

    [Fact]
    public async Task IsEliminated_ClearsAllHeights_ReturnsFalse()
    {
        var heights = new List<HighJumpHeight>
        {
            CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Clear)),
            CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear)),
            CreateHeight(3, 1.60m, 3, CreateAttempt(1, HighJumpAttemptResult.Clear))
        };

        _mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
            .ReturnsAsync(heights);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(3)).ReturnsAsync(heights[2]);

        var result = await _service.IsEliminatedAsync(1, 1);

        Assert.False(result);
    }

    [Fact]
    public async Task IsEliminated_TwoFailsThenClear_ResetsCount_ReturnsFalse()
    {
        var heights = new List<HighJumpHeight>
        {
            CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear)),
            CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Clear))
        };

        _mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
            .ReturnsAsync(heights);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);

        var result = await _service.IsEliminatedAsync(1, 1);

        Assert.False(result);
    }

    [Fact]
    public async Task IsEliminated_NoAttempts_ReturnsFalse()
    {
        var heights = new List<HighJumpHeight>
        {
            CreateHeight(1, 1.50m, 1)
        };

        _mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
            .ReturnsAsync(heights);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);

        var result = await _service.IsEliminatedAsync(1, 1);

        Assert.False(result);
    }

    [Fact]
    public async Task IsEliminated_PassedHeight_FailCountDoesNotReset()
    {
        // If an athlete passes a height, fails should continue accumulating
        var heights = new List<HighJumpHeight>
        {
            CreateHeight(1, 1.50m, 1, CreateAttempt(1, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Fail, HighJumpAttemptResult.Pass)),
            // Height 2: passed (no attempt recorded in this test)
            CreateHeight(2, 1.55m, 2, CreateAttempt(1, HighJumpAttemptResult.Fail))
        };

        _mockUow.Setup(u => u.HighJumpHeights.GetByTournamentEventLevelAsync(1))
            .ReturnsAsync(heights);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(1)).ReturnsAsync(heights[0]);
        _mockUow.Setup(u => u.HighJumpHeights.GetWithAttemptsAsync(2)).ReturnsAsync(heights[1]);

        var result = await _service.IsEliminatedAsync(1, 1);

        // 2 fails at height 1 (pass doesn't reset) + 1 fail at height 2 = 3 consecutive fails
        Assert.True(result);
    }
}
