#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Common;
using Baya.Application.Models.Identity;
using Baya.Domain.Entities.User;
using Mediator;

namespace Baya.Application.Features.Identity.Commands.SelectRole;

internal sealed class SelectRoleCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork,
    IDateTimeProvider clock)
    : IRequestHandler<SelectRoleCommand, OperationResult<MeResult>>
{
    public async ValueTask<OperationResult<MeResult>> Handle(SelectRoleCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<MeResult>.UnauthorizedResult("Not authenticated.");

        var roleName = request.Role.Trim().ToLowerInvariant();
        if (!RoleNames.SelfAssignable.Contains(roleName))
            return OperationResult<MeResult>.ForbiddenResult("Only the customer or nurse role can be self-selected.");

        var role = await unitOfWork.UserAccountRepository.GetRoleByNameAsync(roleName, cancellationToken);
        if (role is null)
            return OperationResult<MeResult>.FailureResult("The requested role is not available.");

        var now = clock.UtcNow;
        var existing = await unitOfWork.UserAccountRepository.GetUserRoleIncludingRevokedAsync(userId, role.Id, cancellationToken);

        if (existing is null)
        {
            await unitOfWork.UserAccountRepository.AddUserRoleAsync(new UserRole
            {
                UserId = userId,
                RoleId = role.Id,
                GrantedById = userId,
                GrantedAt = now,
                CreatedUserRoleDate = now.UtcDateTime
            }, cancellationToken);
        }
        else if (existing.RevokedAt is not null)
        {
            // Re-activate the historical grant instead of colliding with the composite key.
            existing.RevokedAt = null;
            existing.GrantedById = userId;
            existing.GrantedAt = now;
        }
        // else: already held — idempotent success.

        await unitOfWork.CommitAsync();

        var snapshot = await unitOfWork.UserAccountRepository.GetAccountSnapshotAsync(userId, cancellationToken);
        return snapshot is null
            ? OperationResult<MeResult>.NotFoundResult("User not found.")
            : OperationResult<MeResult>.SuccessResult(IdentityDefaults.ToMeResult(snapshot));
    }
}