using FluentAssertions;
using NSubstitute;
using Knot.Modules.Auth.Application.Abstractions;
using Knot.Modules.Auth.Application.Users.Login;
using Knot.Modules.Auth.Domain;
using Knot.Shared.Kernel;
using Knot.Modules.Auth.Application.Users.Auth;
using Xunit;
using System.Threading;
using System.Threading.Tasks;

namespace Knot.Modules.Auth.UnitTests;

public class LoginUserCommandHandlerTests
{
    private readonly IUserRepository _userRepository;
    private readonly IJwtTokenProvider _tokenProvider;
    private readonly LoginUserCommandHandler _handler;

    public LoginUserCommandHandlerTests()
    {
        _userRepository = Substitute.For<IUserRepository>();
        _tokenProvider = Substitute.For<IJwtTokenProvider>();
        _handler = new LoginUserCommandHandler(_userRepository, _tokenProvider);
    }

    [Fact]
    public async Task Handle_ShouldReturnToken_WhenCredentialsAreValid()
    {
        // Arrange
        var password = "password123";
        var passwordHash = BCrypt.Net.BCrypt.HashPassword(password);
        var user = User.Create("testuser", passwordHash, "Test User", null, null);

        var command = new LoginUserCommand("testuser", password);

        _userRepository.GetByUsernameAsync(command.Username, Arg.Any<CancellationToken>())
            .Returns(user);

        _tokenProvider.Generate(user).Returns("valid-jwt-token");

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

        // Assert
        result.IsSuccess.Should().BeTrue();
        result.Value.Token.Should().Be("valid-jwt-token");
        result.Value.User.Username.Should().Be("testuser");
    }

    [Fact]
    public async Task Handle_ShouldReturnFailure_WhenUserDoesNotExist()
    {
        // Arrange
        var command = new LoginUserCommand("nonexistent", "password123");
        _userRepository.GetByUsernameAsync(command.Username, Arg.Any<CancellationToken>())
            .Returns((User)null!);

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

        // Assert
        result.IsFailure.Should().BeTrue();
        result.Error.Code.Should().Be(AuthErrors.IdentityInvalidCredentials.Code);
    }

    [Fact]
    public async Task Handle_ShouldReturnFailure_WhenPasswordIsInvalid()
    {
        // Arrange
        var correctPassword = "correctPassword";
        var passwordHash = BCrypt.Net.BCrypt.HashPassword(correctPassword);
        var user = User.Create("testuser", passwordHash, "Test User", null, null);

        var command = new LoginUserCommand("testuser", "wrongPassword");

        _userRepository.GetByUsernameAsync(command.Username, Arg.Any<CancellationToken>())
            .Returns(user);

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

        // Assert
        result.IsFailure.Should().BeTrue();
        result.Error.Code.Should().Be(AuthErrors.IdentityInvalidCredentials.Code);
    }
}

