using FluentAssertions;
using MembershipService.API.Application.Commands;
using MembershipService.Domain.AggregatesModel.LevelAggregate;
using MembershipService.Domain.SeedWork;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Xunit;

namespace MembershipService.UnitTests.Handlers;

/// <summary>
/// EN: Unit tests for UpdateLevelDefinitionCommandHandler.
/// VI: Unit tests cho UpdateLevelDefinitionCommandHandler.
/// </summary>
public class UpdateLevelDefinitionCommandHandlerTests
{
    private readonly ILevelDefinitionRepository _repository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly ILogger<UpdateLevelDefinitionCommandHandler> _logger;
    private readonly UpdateLevelDefinitionCommandHandler _handler;

    public UpdateLevelDefinitionCommandHandlerTests()
    {
        _repository = Substitute.For<ILevelDefinitionRepository>();
        _unitOfWork = Substitute.For<IUnitOfWork>();
        _logger = Substitute.For<ILogger<UpdateLevelDefinitionCommandHandler>>();

        _repository.UnitOfWork.Returns(_unitOfWork);

        _handler = new UpdateLevelDefinitionCommandHandler(_repository, _logger);
    }

    [Fact]
    public async Task Handle_ValidUpdate_ShouldUpdateLevelDefinition()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(1, "Bronze", 0, "Old description");
        
        var command = new UpdateLevelDefinitionCommand
        {
            Id = levelId,
            Name = "Bronze Elite",
            RequiredExp = 50,
            Description = "Updated description"
        };

        _repository.GetAsync(levelId).Returns(existingLevel);
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.Name.Should().Be("Bronze Elite");
        result.RequiredExp.Should().Be(50);
        result.Description.Should().Be("Updated description");

        _repository.Received(1).Update(Arg.Any<LevelDefinition>());
        await _unitOfWork.Received(1).SaveEntitiesAsync(Arg.Any<CancellationToken>());
    }

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

        _repository.GetAsync(command.Id).Returns((LevelDefinition?)null);

        // Act
        var act = () => _handler.Handle(command, CancellationToken.None);

        // Assert
        await act.Should().ThrowAsync<KeyNotFoundException>()
            .WithMessage("*not found*");

        _repository.DidNotReceive().Update(Arg.Any<LevelDefinition>());
    }

    [Fact]
    public async Task Handle_PartialUpdate_ShouldOnlyUpdateProvidedFields()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(2, "Silver", 100, "Original description", null, "#C0C0C0");
        
        var command = new UpdateLevelDefinitionCommand
        {
            Id = levelId,
            Name = "Silver Plus"
            // Not updating RequiredExp, Description, etc.
        };

        _repository.GetAsync(levelId).Returns(existingLevel);
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.Name.Should().Be("Silver Plus");
        result.RequiredExp.Should().Be(100); // Unchanged
        result.BadgeColor.Should().Be("#C0C0C0"); // Unchanged
    }

    [Fact]
    public async Task Handle_ClearDescription_ShouldSetToNull()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(3, "Gold", 300, "Has description");
        
        var command = new UpdateLevelDefinitionCommand
        {
            Id = levelId,
            ClearDescription = true
        };

        _repository.GetAsync(levelId).Returns(existingLevel);
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.Description.Should().BeNull();
    }

    [Fact]
    public async Task Handle_UpdateBadgeColor_ShouldUpdateColor()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(4, "Platinum", 600, null, null, "#E5E4E2");
        
        var command = new UpdateLevelDefinitionCommand
        {
            Id = levelId,
            BadgeColor = "#00CED1"
        };

        _repository.GetAsync(levelId).Returns(existingLevel);
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>()).Returns(true);

        // Act
        var result = await _handler.Handle(command, CancellationToken.None);

        // Assert
        result.Should().NotBeNull();
        result.BadgeColor.Should().Be("#00CED1");
    }
}
