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

namespace MembershipService.FunctionalTests.Controllers;

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

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

    #region POST /api/v1/levels - Create Level

    [Fact]
    public async Task CreateLevel_WithAuth_ShouldReturn201()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 10,
            Name = "Test Level",
            RequiredExp = 5000,
            Description = "Test level for functional tests",
            BadgeColor = "#FF5733"
        };

        // Act
        var response = await client.PostAsJsonAsync("/api/v1/levels", command);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Created);
        
        var result = await response.Content.ReadFromJsonAsync<CreateLevelDefinitionResult>();
        result.Should().NotBeNull();
        result!.LevelNumber.Should().Be(10);
        result.Name.Should().Be("Test Level");
        result.RequiredExp.Should().Be(5000);
    }

    [Fact]
    public async Task CreateLevel_WithoutAuth_ShouldReturn401()
    {
        // Arrange
        var client = _factory.CreateClient(); // No auth
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 11,
            Name = "Unauthorized Level",
            RequiredExp = 6000
        };

        // Act
        var response = await client.PostAsJsonAsync("/api/v1/levels", command);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task CreateLevel_DuplicateLevelNumber_ShouldReturn409()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        
        // First create a level
        var firstCommand = new CreateLevelDefinitionCommand
        {
            LevelNumber = 50, // Use unique number to avoid conflicts with other tests
            Name = "First Level",
            RequiredExp = 500
        };
        var firstResponse = await client.PostAsJsonAsync("/api/v1/levels", firstCommand);
        firstResponse.StatusCode.Should().Be(HttpStatusCode.Created);
        
        // Now try to create duplicate
        var duplicateCommand = new CreateLevelDefinitionCommand
        {
            LevelNumber = 50, // Same level number - should conflict
            Name = "Duplicate",
            RequiredExp = 500
        };

        // Act
        var response = await client.PostAsJsonAsync("/api/v1/levels", duplicateCommand);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Conflict);
    }

    #endregion

    #region PUT /api/v1/levels/{id} - Update Level

    [Fact]
    public async Task UpdateLevel_WithAuth_ShouldReturn200()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        
        // First create a level to update
        var createCommand = new CreateLevelDefinitionCommand
        {
            LevelNumber = 60,
            Name = "ToUpdate",
            RequiredExp = 6000,
            Description = "Original description"
        };
        var createResponse = await client.PostAsJsonAsync("/api/v1/levels", createCommand);
        createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
        var createdLevel = await createResponse.Content.ReadFromJsonAsync<CreateLevelDefinitionResult>();
        
        var updateCommand = new UpdateLevelDefinitionCommand
        {
            Id = createdLevel!.Id,
            Name = "Updated Level",
            Description = "Updated description"
        };

        // Act
        var response = await client.PutAsJsonAsync($"/api/v1/levels/{createdLevel.Id}", updateCommand);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.OK);
        
        var result = await response.Content.ReadFromJsonAsync<UpdateLevelDefinitionResult>();
        result.Should().NotBeNull();
        result!.Name.Should().Be("Updated Level");
        result.Description.Should().Be("Updated description");
    }

    [Fact]
    public async Task UpdateLevel_WithoutAuth_ShouldReturn401()
    {
        // Arrange
        var client = _factory.CreateClient(); // No auth
        var randomId = Guid.NewGuid();
        var command = new UpdateLevelDefinitionCommand
        {
            Id = randomId,
            Name = "Unauthorized Update"
        };

        // Act
        var response = await client.PutAsJsonAsync($"/api/v1/levels/{randomId}", command);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task UpdateLevel_NotFound_ShouldReturn404()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        var nonExistentId = Guid.NewGuid();
        var command = new UpdateLevelDefinitionCommand
        {
            Id = nonExistentId,
            Name = "NonExistent"
        };

        // Act
        var response = await client.PutAsJsonAsync($"/api/v1/levels/{nonExistentId}", command);
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
    }

    #endregion

    #region DELETE /api/v1/levels/{id} - Deactivate Level

    [Fact]
    public async Task DeactivateLevel_WithAuth_ShouldReturn204()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        
        // First create a level to deactivate
        var createCommand = new CreateLevelDefinitionCommand
        {
            LevelNumber = 99,
            Name = "ToDeactivate",
            RequiredExp = 9999
        };
        var createResponse = await client.PostAsJsonAsync("/api/v1/levels", createCommand);
        var createdLevel = await createResponse.Content.ReadFromJsonAsync<CreateLevelDefinitionResult>();

        // Act
        var response = await client.DeleteAsync($"/api/v1/levels/{createdLevel!.Id}");
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NoContent);
    }

    [Fact]
    public async Task DeactivateLevel_WithoutAuth_ShouldReturn401()
    {
        // Arrange
        var client = _factory.CreateClient(); // No auth
        var randomId = Guid.NewGuid();

        // Act
        var response = await client.DeleteAsync($"/api/v1/levels/{randomId}");
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
    }

    [Fact]
    public async Task DeactivateLevel_NotFound_ShouldReturn404()
    {
        // Arrange
        var client = _factory.CreateAuthenticatedClient();
        var nonExistentId = Guid.NewGuid();

        // Act
        var response = await client.DeleteAsync($"/api/v1/levels/{nonExistentId}");
        
        // Assert
        response.StatusCode.Should().Be(HttpStatusCode.NotFound);
    }

    #endregion

    #region Helper DTOs

    private class LevelDto
    {
        public Guid Id { get; set; }
        public int LevelNumber { get; set; }
        public string Name { get; set; } = string.Empty;
    }

    #endregion
}
