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

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

public class UpdateUserRoleCommandHandler(
    IUserTenantRoleRepository userTenantRoleRepository,
    IUserRepository userRepository)
    : IRequestHandler<UpdateUserRoleCommand, UserWithRoleDto>
{
    public async Task<UserWithRoleDto> Handle(UpdateUserRoleCommand request, CancellationToken cancellationToken)
    {
        // Validate user exists
        var user = await userRepository.GetByIdAsync(request.UserId, cancellationToken);
        if (user == null)
            throw new InvalidOperationException("User not found");

        // Parse and validate new role
        if (!Enum.TryParse<TenantRole>(request.NewRole, out var newRole))
            throw new ArgumentException($"Invalid role: {request.NewRole}");

        // Prevent manual assignment of AIAgent role
        if (newRole == TenantRole.AIAgent)
            throw new InvalidOperationException("AIAgent role cannot be assigned manually");

        // Get existing role
        var existingRole = await userTenantRoleRepository.GetByUserAndTenantAsync(
            request.UserId,
            request.TenantId,
            cancellationToken);

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

        // Rule 1: Cannot self-demote from TenantOwner
        if (request.OperatorUserId == request.UserId &&
            existingRole.Role == TenantRole.TenantOwner &&
            newRole != TenantRole.TenantOwner)
        {
            throw new InvalidOperationException(
                "Cannot self-demote from TenantOwner role. Another owner must perform this action.");
        }

        // Rule 2: Cannot remove last TenantOwner
        if (existingRole.Role == TenantRole.TenantOwner && newRole != TenantRole.TenantOwner)
        {
            var ownerCount = await userTenantRoleRepository.CountByTenantAndRoleAsync(
                request.TenantId,
                TenantRole.TenantOwner,
                cancellationToken);

            if (ownerCount <= 1)
            {
                throw new InvalidOperationException(
                    "Cannot remove the last TenantOwner. Assign another owner first.");
            }
        }

        // Update role
        existingRole.UpdateRole(newRole, request.OperatorUserId);
        await userTenantRoleRepository.UpdateAsync(existingRole, cancellationToken);

        // Return updated user with role DTO
        return new UserWithRoleDto(
            UserId: user.Id,
            Email: user.Email.Value,
            FullName: user.FullName.Value,
            Role: newRole.ToString(),
            AssignedAt: existingRole.AssignedAt,
            EmailVerified: user.IsEmailVerified
        );
    }
}
