using System.Net;
using System.Net.Http.Json;
using FluentAssertions;
using MembershipService.API.Application.Queries;
using Xunit;

namespace MembershipService.FunctionalTests.Controllers;

/// <summary>
/// EN: Functional tests for LevelsController.
/// VI: Functional tests cho LevelsController.
/// </summary>
[Collection("Sequential")]
public class LevelsControllerTests : IClassFixture<CustomWebApplicationFactory>
{
    private readonly CustomWebApplicationFactory _factory;

    public LevelsControllerTests(CustomWebApplicationFactory factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task GetLevels_PublicEndpoint_ShouldReturnOkWithoutAuth()
    {
        // Arrange
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
    }

    [Fact]
    public async Task GetLevels_WithSeededData_ShouldReturnLevelsList()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        levels.Should().NotBeNull();
        levels!.Should().HaveCountGreaterOrEqualTo(5);
    }

    [Fact]
    public async Task GetLevels_ShouldReturnOrderedByLevelNumber()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        levels.Should().NotBeNull();
        levels!.Should().BeInAscendingOrder(l => l.LevelNumber);
    }

    [Fact]
    public async Task GetLevels_ShouldReturnCorrectLevelData()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        levels.Should().NotBeNull();
        if (levels == null || !levels.Any()) return;
        
        var bronze = levels.FirstOrDefault(l => l.LevelNumber == 1);
        bronze.Should().NotBeNull();
        bronze!.Name.Should().Be("Bronze");
        bronze.RequiredExp.Should().Be(0);
        bronze.BadgeColor.Should().Be("#CD7F32");
    }

    [Fact]
    public async Task GetLevels_IncludeInactiveFalse_ShouldOnlyReturnActive()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels?includeInactive=false");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        levels.Should().NotBeNull();
        if (levels != null && levels.Any())
        {
            levels.Should().AllSatisfy(l => l.IsActive.Should().BeTrue());
        }
    }

    [Fact]
    public async Task GetLevels_ShouldReturnExpectedExpThresholds()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        levels.Should().NotBeNull();
        if (levels == null || !levels.Any()) return;

        var expectedThresholds = new Dictionary<int, int>
        {
            { 1, 0 },    // Bronze
            { 2, 100 },  // Silver
            { 3, 300 },  // Gold
            { 4, 600 },  // Platinum
            { 5, 1000 }  // Diamond
        };

        foreach (var expected in expectedThresholds)
        {
            var level = levels.FirstOrDefault(l => l.LevelNumber == expected.Key);
            if (level != null)
            {
                level.RequiredExp.Should().Be(expected.Value);
            }
        }
    }

    [Fact]
    public async Task GetLevels_EachLevelShouldHaveUniqueId()
    {
        // Arrange
        await _factory.SeedLevelDefinitionsAsync();
        var client = _factory.CreateClient();

        // Act
        var response = await client.GetAsync("/api/v1/levels");
        var levels = await response.Content.ReadFromJsonAsync<List<LevelDefinitionDto>>();

        // Assert
        levels.Should().NotBeNull();
        if (levels != null && levels.Any())
        {
            var ids = levels.Select(l => l.Id).ToList();
            ids.Should().OnlyHaveUniqueItems();
        }
    }
}
