using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Features.Identity.Commands.SelectRole;
using Baya.Application.Models.Identity;
using Baya.Domain.Entities.User;
using NSubstitute;
using NSubstitute.ReturnsExtensions;

namespace Baya.Test.Foundation.Identity;

public class SelectRoleCommandHandlerTests
{
    private static readonly DateTimeOffset Now = new(2026, 7, 1, 12, 0, 0, TimeSpan.Zero);

    private readonly ICurrentUser _currentUser = Substitute.For<ICurrentUser>();
    private readonly IUnitOfWork _unitOfWork = Substitute.For<IUnitOfWork>();
    private readonly IUserAccountRepository _accounts = Substitute.For<IUserAccountRepository>();
    private readonly IDateTimeProvider _clock = Substitute.For<IDateTimeProvider>();

    private SelectRoleCommandHandler CreateHandler()
    {
        _clock.UtcNow.Returns(Now);
        _currentUser.UserId.Returns(7);
        _unitOfWork.UserAccountRepository.Returns(_accounts);
        _accounts.GetAccountSnapshotAsync(7, Arg.Any<CancellationToken>())
            .Returns(new UserAccountSnapshot(7, "09123456789", null, null, null, true, ["customer"]));

        return new SelectRoleCommandHandler(_currentUser, _unitOfWork, _clock);
    }

    [Fact]
    public async Task Handle_AdminSubRole_IsForbidden()
    {
        // Arrange
        var handler = CreateHandler();

        // Act
        var result = await handler.Handle(new SelectRoleCommand("super_admin"), CancellationToken.None);

        // Assert
        Assert.False(result.IsSuccess);
        Assert.True(result.IsForbidden);
        await _accounts.DidNotReceive().AddUserRoleAsync(Arg.Any<UserRole>(), Arg.Any<CancellationToken>());
        await _unitOfWork.DidNotReceive().CommitAsync();
    }

    [Fact]
    public async Task Handle_NewCustomerRole_GrantsWithSelfAudit()
    {
        // Arrange
        _accounts.GetRoleByNameAsync("customer", Arg.Any<CancellationToken>()).Returns(new Role { Id = 3, Name = "customer" });
        _accounts.GetUserRoleIncludingRevokedAsync(7, 3, Arg.Any<CancellationToken>()).ReturnsNull();
        var handler = CreateHandler();

        // Act
        var result = await handler.Handle(new SelectRoleCommand("Customer"), CancellationToken.None);

        // Assert — case-insensitive input, granted_by = self, masked phone in the payload.
        Assert.True(result.IsSuccess);
        Assert.Contains("customer", result.Result.Roles);
        Assert.DoesNotContain("09123456789", result.Result.Phone);
        await _accounts.Received(1).AddUserRoleAsync(
            Arg.Is<UserRole>(ur => ur.UserId == 7 && ur.RoleId == 3 && ur.GrantedById == 7 && ur.GrantedAt == Now),
            Arg.Any<CancellationToken>());
        await _unitOfWork.Received(1).CommitAsync();
    }

    [Fact]
    public async Task Handle_RoleAlreadyHeld_IsIdempotent()
    {
        // Arrange
        _accounts.GetRoleByNameAsync("customer", Arg.Any<CancellationToken>()).Returns(new Role { Id = 3, Name = "customer" });
        _accounts.GetUserRoleIncludingRevokedAsync(7, 3, Arg.Any<CancellationToken>())
            .Returns(new UserRole { UserId = 7, RoleId = 3, GrantedAt = Now.AddDays(-1) });
        var handler = CreateHandler();

        // Act
        var result = await handler.Handle(new SelectRoleCommand("customer"), CancellationToken.None);

        // Assert
        Assert.True(result.IsSuccess);
        await _accounts.DidNotReceive().AddUserRoleAsync(Arg.Any<UserRole>(), Arg.Any<CancellationToken>());
    }

    [Fact]
    public async Task Handle_RevokedGrant_IsReactivatedNotDuplicated()
    {
        // Arrange
        var revoked = new UserRole { UserId = 7, RoleId = 3, GrantedAt = Now.AddDays(-10), RevokedAt = Now.AddDays(-5) };
        _accounts.GetRoleByNameAsync("nurse", Arg.Any<CancellationToken>()).Returns(new Role { Id = 3, Name = "nurse" });
        _accounts.GetUserRoleIncludingRevokedAsync(7, 3, Arg.Any<CancellationToken>()).Returns(revoked);
        var handler = CreateHandler();

        // Act
        var result = await handler.Handle(new SelectRoleCommand("nurse"), CancellationToken.None);

        // Assert
        Assert.True(result.IsSuccess);
        Assert.Null(revoked.RevokedAt);
        Assert.Equal(Now, revoked.GrantedAt);
        Assert.Equal(7, revoked.GrantedById);
        await _accounts.DidNotReceive().AddUserRoleAsync(Arg.Any<UserRole>(), Arg.Any<CancellationToken>());
    }
}