using NSubstitute;
using TeleWave.Application.Auth;
using TeleWave.Application.Auth.Register;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using Xunit;

namespace TeleWave.Application.Tests.Auth;

public class RegisterCommandHandlerTests
{
    private readonly IIdentityService _identityService = Substitute.For<IIdentityService>();
    private readonly IJwtTokenService _jwtTokenService = Substitute.For<IJwtTokenService>();
    private readonly IRefreshTokenService _refreshTokenService =
        Substitute.For<IRefreshTokenService>();
    private readonly ISiteSettings _siteSettings = Substitute.For<ISiteSettings>();

    public RegisterCommandHandlerTests()
    {
        _siteSettings.IsRegistrationEnabledAsync(Arg.Any<CancellationToken>()).Returns(true);
    }

    private RegisterCommandHandler CreateHandler() =>
        new(_identityService, _jwtTokenService, _refreshTokenService, _siteSettings);

    [Fact]
    public async Task Handle_WithNewUserName_CreatesUserAndReturnsAuthResult()
    {
        var userId = Guid.NewGuid();
        _identityService
            .CreateUserAsync("bob", "password123", Arg.Any<CancellationToken>())
            .Returns(Result.Success(userId));
        _identityService
            .GetProfileAsync(userId, Arg.Any<CancellationToken>())
            .Returns(new CurrentUserProfile(userId, "bob", "user", IsBlocked: false));
        _jwtTokenService
            .GenerateAccessToken(Arg.Any<AuthenticatedUser>())
            .Returns(("access-token", DateTimeOffset.UtcNow.AddMinutes(15)));
        _refreshTokenService
            .IssueAsync(userId, Arg.Any<CancellationToken>())
            .Returns(new IssuedRefreshToken("refresh-token", DateTimeOffset.UtcNow.AddDays(30)));

        var result = await CreateHandler()
            .Handle(new RegisterCommand("bob", "password123"), CancellationToken.None);

        Assert.True(result.IsSuccess);
        Assert.Equal("bob", result.Value.User.UserName);
    }

    [Fact]
    public async Task Handle_WhenRegistrationDisabled_ReturnsForbidden()
    {
        _siteSettings.IsRegistrationEnabledAsync(Arg.Any<CancellationToken>()).Returns(false);

        var result = await CreateHandler()
            .Handle(new RegisterCommand("bob", "password123"), CancellationToken.None);

        Assert.False(result.IsSuccess);
        Assert.Equal(AuthErrors.RegistrationDisabled, result.Error);
        await _identityService
            .DidNotReceive()
            .CreateUserAsync(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_WithTakenUserName_ReturnsFailure()
    {
        _identityService
            .CreateUserAsync("bob", "password123", Arg.Any<CancellationToken>())
            .Returns(Result.Failure<Guid>(AuthErrors.UserNameTaken));

        var result = await CreateHandler()
            .Handle(new RegisterCommand("bob", "password123"), CancellationToken.None);

        Assert.False(result.IsSuccess);
        Assert.Equal(AuthErrors.UserNameTaken, result.Error);
    }
}
