using NSubstitute;
using TeleWave.Application.Admin.Roles;
using TeleWave.Application.Admin.Roles.CreateRole;
using TeleWave.Application.Admin.Roles.DeleteRole;
using TeleWave.Application.Admin.Roles.ListRoles;
using TeleWave.Application.Admin.Roles.UpdateRole;
using TeleWave.Application.Admin.Users;
using TeleWave.Application.Admin.Users.DeleteUser;
using TeleWave.Application.Admin.Users.ResetPassword;
using TeleWave.Application.Admin.Users.UnblockUser;
using TeleWave.Application.Auth;
using TeleWave.Application.Auth.ChangePassword;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Common.Models;
using Xunit;

namespace TeleWave.Application.Tests.Admin;

public class DelegatingHandlersTests
{
    private readonly IRoleService _roles = Substitute.For<IRoleService>();
    private readonly IIdentityService _identity = Substitute.For<IIdentityService>();
    private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();

    [Fact]
    public async Task Roles_HandlersDelegateToService()
    {
        var id = Guid.NewGuid();
        var dto = new RoleDto(id, "editor", false);
        _roles.CreateRoleAsync("editor", Arg.Any<CancellationToken>()).Returns(Result.Success(dto));
        _roles
            .UpdateRoleAsync(id, "editor", Arg.Any<CancellationToken>())
            .Returns(Result.Success(dto));
        _roles.DeleteRoleAsync(id, Arg.Any<CancellationToken>()).Returns(Result.Success());
        _roles.ListRolesAsync(Arg.Any<CancellationToken>()).Returns(new List<RoleDto> { dto });

        Assert.True(
            (
                await new CreateRoleCommandHandler(_roles).Handle(
                    new CreateRoleCommand("editor"),
                    default
                )
            ).IsSuccess
        );
        Assert.True(
            (
                await new UpdateRoleCommandHandler(_roles).Handle(
                    new UpdateRoleCommand(id, "editor"),
                    default
                )
            ).IsSuccess
        );
        Assert.True(
            (
                await new DeleteRoleCommandHandler(_roles).Handle(
                    new DeleteRoleCommand(id),
                    default
                )
            ).IsSuccess
        );
        Assert.Single(
            await new ListRolesQueryHandler(_roles).Handle(new ListRolesQuery(), default)
        );
    }

    [Fact]
    public async Task UnblockUser_And_ResetPassword_Delegate()
    {
        var id = Guid.NewGuid();
        _identity.UnblockUserAsync(id, Arg.Any<CancellationToken>()).Returns(Result.Success());
        _identity
            .ResetPasswordAsync(id, "Password1", Arg.Any<CancellationToken>())
            .Returns(Result.Success());

        Assert.True(
            (
                await new UnblockUserCommandHandler(_identity).Handle(
                    new UnblockUserCommand(id),
                    default
                )
            ).IsSuccess
        );
        Assert.True(
            (
                await new ResetUserPasswordCommandHandler(_identity).Handle(
                    new ResetUserPasswordCommand(id, "Password1"),
                    default
                )
            ).IsSuccess
        );
        await _identity
            .Received(1)
            .ResetPasswordAsync(id, "Password1", Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task DeleteUser_CannotDeleteSelf()
    {
        var me = Guid.NewGuid();
        _currentUser.UserId.Returns(me);

        var result = await new DeleteUserCommandHandler(_identity, _currentUser).Handle(
            new DeleteUserCommand(me),
            default
        );

        Assert.Equal(UserErrors.CannotDeleteSelf, result.Error);
        await _identity
            .DidNotReceive()
            .DeleteUserAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task DeleteUser_OtherUser_Delegates()
    {
        _currentUser.UserId.Returns(Guid.NewGuid());
        var target = Guid.NewGuid();
        _identity.DeleteUserAsync(target, Arg.Any<CancellationToken>()).Returns(Result.Success());

        var result = await new DeleteUserCommandHandler(_identity, _currentUser).Handle(
            new DeleteUserCommand(target),
            default
        );

        Assert.True(result.IsSuccess);
    }

    [Fact]
    public async Task ChangePassword_Unauthorized_WhenNoCurrentUser()
    {
        _currentUser.UserId.Returns((Guid?)null);

        var result = await new ChangePasswordCommandHandler(_identity, _currentUser).Handle(
            new ChangePasswordCommand("old", "New12345"),
            default
        );

        Assert.Equal(AuthErrors.Unauthorized, result.Error);
    }

    [Fact]
    public async Task ChangePassword_Delegates_WhenAuthenticated()
    {
        var me = Guid.NewGuid();
        _currentUser.UserId.Returns(me);
        _identity
            .ChangePasswordAsync(me, "old", "New12345", Arg.Any<CancellationToken>())
            .Returns(Result.Success());

        var result = await new ChangePasswordCommandHandler(_identity, _currentUser).Handle(
            new ChangePasswordCommand("old", "New12345"),
            default
        );

        Assert.True(result.IsSuccess);
    }
}
