using Xunit;
using Moq;
using FluentAssertions;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using IamService.API.Application.Commands.Roles;
using IamService.Domain.AggregatesModel.RoleAggregate;
using IamService.Domain.Exceptions;

namespace IamService.UnitTests.Application.Commands.Roles;

/// <summary>
/// EN: Unit tests for CreateRoleCommandHandler.
/// VI: Unit tests cho CreateRoleCommandHandler.
/// </summary>
public class CreateRoleCommandHandlerTests
{
    private readonly Mock<RoleManager<ApplicationRole>> _roleManagerMock;
    private readonly Mock<ILogger<CreateRoleCommandHandler>> _loggerMock;
    private readonly CreateRoleCommandHandler _handler;

    public CreateRoleCommandHandlerTests()
    {
        // EN: Setup RoleManager mock
        // VI: Thiết lập mock cho RoleManager
        var roleStoreMock = new Mock<IRoleStore<ApplicationRole>>();
        _roleManagerMock = new Mock<RoleManager<ApplicationRole>>(
            roleStoreMock.Object,
            null!, null!, null!, null!);
        
        _loggerMock = new Mock<ILogger<CreateRoleCommandHandler>>();

        _handler = new CreateRoleCommandHandler(
            _roleManagerMock.Object,
            _loggerMock.Object);
    }

    [Fact]
    public async Task Handle_ValidCommand_CreatesRoleAndReturnsResult()
    {
        // Arrange
        var command = new CreateRoleCommand("Administrator", "System administrator role");

        _roleManagerMock
            .Setup(r => r.FindByNameAsync(command.Name))
            .ReturnsAsync((ApplicationRole?)null);

        _roleManagerMock
            .Setup(r => r.CreateAsync(It.IsAny<ApplicationRole>()))
            .ReturnsAsync(IdentityResult.Success);

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

        // Assert
        result.Should().NotBeNull();
        result.Id.Should().NotBeEmpty();
        result.Name.Should().Be("Administrator");
        result.Description.Should().Be("System administrator role");
        result.CreatedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1));
    }

    [Fact]
    public async Task Handle_ValidCommand_CallsRoleManagerCreate()
    {
        // Arrange
        var command = new CreateRoleCommand("Editor", null);

        _roleManagerMock
            .Setup(r => r.FindByNameAsync(command.Name))
            .ReturnsAsync((ApplicationRole?)null);

        _roleManagerMock
            .Setup(r => r.CreateAsync(It.IsAny<ApplicationRole>()))
            .ReturnsAsync(IdentityResult.Success);

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

        // Assert
        _roleManagerMock.Verify(r => r.CreateAsync(It.Is<ApplicationRole>(
            role => role.Name == "Editor")), Times.Once);
    }

    [Fact]
    public async Task Handle_RoleAlreadyExists_ThrowsDomainException()
    {
        // Arrange
        var command = new CreateRoleCommand("ExistingRole", null);
        var existingRole = new ApplicationRole("ExistingRole");

        _roleManagerMock
            .Setup(r => r.FindByNameAsync(command.Name))
            .ReturnsAsync(existingRole);

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

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

        _roleManagerMock.Verify(r => r.CreateAsync(It.IsAny<ApplicationRole>()), Times.Never);
    }

    [Fact]
    public async Task Handle_CreateFails_ThrowsDomainException()
    {
        // Arrange
        var command = new CreateRoleCommand("NewRole", null);

        _roleManagerMock
            .Setup(r => r.FindByNameAsync(command.Name))
            .ReturnsAsync((ApplicationRole?)null);

        _roleManagerMock
            .Setup(r => r.CreateAsync(It.IsAny<ApplicationRole>()))
            .ReturnsAsync(IdentityResult.Failed(new IdentityError { Description = "Creation failed" }));

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

        // Assert
        await act.Should().ThrowAsync<DomainException>()
            .WithMessage("*Failed to create role*");
    }

    [Fact]
    public async Task Handle_WithoutDescription_CreatesRoleWithNullDescription()
    {
        // Arrange
        var command = new CreateRoleCommand("Viewer", null);

        _roleManagerMock
            .Setup(r => r.FindByNameAsync(command.Name))
            .ReturnsAsync((ApplicationRole?)null);

        _roleManagerMock
            .Setup(r => r.CreateAsync(It.IsAny<ApplicationRole>()))
            .ReturnsAsync(IdentityResult.Success);

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

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