using Xunit;
using Moq;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using IamService.API.Application.Commands.Auth;
using IamService.Domain.AggregatesModel.UserAggregate;
using IamService.Domain.Exceptions;

namespace IamService.UnitTests.Application.Commands;

/// <summary>
/// EN: Unit tests for ChangePasswordCommandHandler.
/// VI: Unit tests cho ChangePasswordCommandHandler.
/// </summary>
public class ChangePasswordCommandHandlerTests
{
    private readonly Mock<UserManager<ApplicationUser>> _userManagerMock;
    private readonly Mock<ILogger<ChangePasswordCommandHandler>> _loggerMock;
    private readonly ChangePasswordCommandHandler _handler;

    public ChangePasswordCommandHandlerTests()
    {
        var store = new Mock<IUserStore<ApplicationUser>>();
        _userManagerMock = new Mock<UserManager<ApplicationUser>>(
            store.Object, null!, null!, null!, null!, null!, null!, null!, null!);
        _loggerMock = new Mock<ILogger<ChangePasswordCommandHandler>>();
        _handler = new ChangePasswordCommandHandler(_userManagerMock.Object, _loggerMock.Object);
    }

    [Fact]
    public async Task Handle_WithValidData_ShouldChangePassword()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var user = new ApplicationUser("test@example.com", "John", "Doe");
        var command = new ChangePasswordCommand(userId, "OldPassword123!", "NewPassword123!");

        _userManagerMock
            .Setup(x => x.FindByIdAsync(userId.ToString()))
            .ReturnsAsync(user);

        _userManagerMock
            .Setup(x => x.ChangePasswordAsync(user, command.CurrentPassword, command.NewPassword))
            .ReturnsAsync(IdentityResult.Success);

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

        // Assert
        Assert.True(result.Success);
    }

    [Fact]
    public async Task Handle_WithInvalidUserId_ShouldThrowDomainException()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var command = new ChangePasswordCommand(userId, "OldPassword123!", "NewPassword123!");

        _userManagerMock
            .Setup(x => x.FindByIdAsync(userId.ToString()))
            .ReturnsAsync((ApplicationUser?)null);

        // Act & Assert
        await Assert.ThrowsAsync<DomainException>(() =>
            _handler.Handle(command, CancellationToken.None));
    }

    [Fact]
    public async Task Handle_WithWrongCurrentPassword_ShouldReturnFailure()
    {
        // Arrange
        var userId = Guid.NewGuid();
        var user = new ApplicationUser("test@example.com", "John", "Doe");
        var command = new ChangePasswordCommand(userId, "WrongPassword!", "NewPassword123!");

        _userManagerMock
            .Setup(x => x.FindByIdAsync(userId.ToString()))
            .ReturnsAsync(user);

        _userManagerMock
            .Setup(x => x.ChangePasswordAsync(user, command.CurrentPassword, command.NewPassword))
            .ReturnsAsync(IdentityResult.Failed(
                new IdentityError { Code = "PasswordMismatch", Description = "Incorrect password" }));

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

        // Assert
        Assert.False(result.Success);
        Assert.Contains("Incorrect password", result.Message);
    }
}
