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 DeactivateLevelDefinitionCommandHandler.
/// VI: Unit tests cho DeactivateLevelDefinitionCommandHandler.
/// </summary>
public class DeactivateLevelDefinitionCommandHandlerTests
{
    private readonly ILevelDefinitionRepository _repository;
    private readonly IUnitOfWork _unitOfWork;
    private readonly ILogger<DeactivateLevelDefinitionCommandHandler> _logger;
    private readonly DeactivateLevelDefinitionCommandHandler _handler;

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

        _repository.UnitOfWork.Returns(_unitOfWork);

        _handler = new DeactivateLevelDefinitionCommandHandler(_repository, _logger);
    }

    [Fact]
    public async Task Handle_ValidLevel_ShouldDeactivate()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(5, "Diamond", 1000, "Top tier");
        
        var command = new DeactivateLevelDefinitionCommand(levelId);

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

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

        // Assert
        result.Should().BeTrue();
        existingLevel.IsActive.Should().BeFalse();

        _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 DeactivateLevelDefinitionCommand(Guid.NewGuid());

        _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_AlreadyInactiveLevel_ShouldStillSucceed()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(6, "Master", 2000, "Master tier");
        existingLevel.Deactivate(); // Already inactive

        var command = new DeactivateLevelDefinitionCommand(levelId);

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

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

        // Assert
        result.Should().BeTrue();
        existingLevel.IsActive.Should().BeFalse();
    }

    [Fact]
    public async Task Handle_WithDefaultConstructor_ShouldWork()
    {
        // Arrange
        var levelId = Guid.NewGuid();
        var existingLevel = new LevelDefinition(7, "Legend", 5000, "Legend tier");
        
        var command = new DeactivateLevelDefinitionCommand { Id = levelId };

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

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

        // Assert
        result.Should().BeTrue();
    }
}
