using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users.Events;
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Domain.Repositories;
using MediatR;

namespace ColaFlow.Modules.Identity.Application.Commands.RemoveUserFromTenant;

public class RemoveUserFromTenantCommandHandler(
    IUserTenantRoleRepository userTenantRoleRepository,
    IRefreshTokenRepository refreshTokenRepository,
    IUserRepository userRepository)
    : IRequestHandler<RemoveUserFromTenantCommand, Unit>
{
    public async Task<Unit> Handle(RemoveUserFromTenantCommand request, CancellationToken cancellationToken)
    {
        // Get user's role in tenant
        var userTenantRole = await userTenantRoleRepository.GetByUserAndTenantAsync(
            request.UserId,
            request.TenantId,
            cancellationToken);

        if (userTenantRole == null)
            throw new InvalidOperationException("User is not a member of this tenant");

        // Check if this is the last TenantOwner
        if (await userTenantRoleRepository.IsLastTenantOwnerAsync(request.TenantId, request.UserId, cancellationToken))
        {
            throw new InvalidOperationException("Cannot remove the last TenantOwner from the tenant");
        }

        // Revoke all user's refresh tokens for this tenant
        var userTokens = await refreshTokenRepository.GetByUserAndTenantAsync(
            request.UserId,
            request.TenantId,
            cancellationToken);

        foreach (var token in userTokens.Where(t => !t.RevokedAt.HasValue))
        {
            token.Revoke(request.Reason ?? "User removed from tenant");
        }

        if (userTokens.Any())
        {
            await refreshTokenRepository.UpdateRangeAsync(userTokens, cancellationToken);
        }

        // Raise domain event before deletion
        var user = await userRepository.GetByIdAsync(request.UserId, cancellationToken);
        if (user != null)
        {
            user.RaiseRemovedFromTenantEvent(
                TenantId.Create(request.TenantId),
                request.RemovedBy,
                request.Reason ?? "User removed from tenant"
            );

            await userRepository.UpdateAsync(user, cancellationToken);
        }

        // Remove user's role
        await userTenantRoleRepository.DeleteAsync(userTenantRole, cancellationToken);

        return Unit.Value;
    }
}
