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

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

        _repository.UnitOfWork.Returns(_unitOfWork);

        _handler = new CreateLevelDefinitionCommandHandler(_repository, _logger);
    }

    [Fact]
    public async Task Handle_ValidCommand_ShouldCreateLevelDefinition()
    {
        // Arrange
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 6,
            Name = "Master",
            RequiredExp = 2000,
            Description = "Master level",
            BadgeColor = "#8B00FF"
        };

        _repository.ExistsByLevelNumberAsync(command.LevelNumber)
            .Returns(false);
        _repository.Add(Arg.Any<LevelDefinition>())
            .Returns(x => x.Arg<LevelDefinition>());
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
            .Returns(true);

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

        // Assert
        result.Should().NotBeNull();
        result.LevelNumber.Should().Be(6);
        result.Name.Should().Be("Master");
        result.RequiredExp.Should().Be(2000);
        result.Id.Should().NotBeEmpty();

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

    [Fact]
    public async Task Handle_DuplicateLevelNumber_ShouldThrowInvalidOperationException()
    {
        // Arrange
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 1,
            Name = "Duplicate",
            RequiredExp = 0
        };

        _repository.ExistsByLevelNumberAsync(command.LevelNumber)
            .Returns(true);

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

        // Assert
        await act.Should().ThrowAsync<InvalidOperationException>()
            .WithMessage("*already exists*");

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

    [Fact]
    public async Task Handle_WithOptionalFields_ShouldCreateWithAllFields()
    {
        // Arrange
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 7,
            Name = "Legend",
            RequiredExp = 5000,
            Description = "Legend tier for elite members",
            IconUrl = "https://example.com/legend.png",
            BadgeColor = "#FFD700"
        };

        _repository.ExistsByLevelNumberAsync(command.LevelNumber)
            .Returns(false);
        _repository.Add(Arg.Any<LevelDefinition>())
            .Returns(x => x.Arg<LevelDefinition>());
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
            .Returns(true);

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

        // Assert
        result.Should().NotBeNull();
        result.LevelNumber.Should().Be(7);
        result.Name.Should().Be("Legend");
        result.RequiredExp.Should().Be(5000);
    }

    [Fact]
    public async Task Handle_MinimalCommand_ShouldCreateWithDefaults()
    {
        // Arrange
        var command = new CreateLevelDefinitionCommand
        {
            LevelNumber = 8,
            Name = "Minimal",
            RequiredExp = 100
        };

        _repository.ExistsByLevelNumberAsync(command.LevelNumber)
            .Returns(false);
        _repository.Add(Arg.Any<LevelDefinition>())
            .Returns(x => x.Arg<LevelDefinition>());
        _unitOfWork.SaveEntitiesAsync(Arg.Any<CancellationToken>())
            .Returns(true);

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

        // Assert
        result.Should().NotBeNull();
        result.LevelNumber.Should().Be(8);
        result.Name.Should().Be("Minimal");
    }
}
