using MediatR;
using IamService.Domain.AggregatesModel.PrivilegedAccessAggregate;
using IamService.Domain.Exceptions;

namespace IamService.API.Application.Commands.PrivilegedAccess;

public class RequestPrivilegedAccessCommandHandler : IRequestHandler<RequestPrivilegedAccessCommand, Guid>
{
    private readonly IPrivilegedAccessRepository _repository;
    public RequestPrivilegedAccessCommandHandler(IPrivilegedAccessRepository repository) => _repository = repository;

    public async Task<Guid> Handle(RequestPrivilegedAccessCommand request, CancellationToken cancellationToken)
    {
        // Check if user already has active grant for this role
        var hasActive = await _repository.HasActiveGrantAsync(request.UserId, request.RoleId, cancellationToken);
        if (hasActive)
            throw new DomainException($"User already has active privileged access for role {request.RoleId}");

        var grant = PrivilegedAccessGrant.Create(
            request.UserId,
            request.RoleId,
            request.ResourceScope,
            request.Reason,
            request.GrantedByUserId,
            request.DurationMinutes);

        _repository.Add(grant);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return grant.Id;
    }
}

public class RevokePrivilegedAccessCommandHandler : IRequestHandler<RevokePrivilegedAccessCommand, Unit>
{
    private readonly IPrivilegedAccessRepository _repository;
    public RevokePrivilegedAccessCommandHandler(IPrivilegedAccessRepository repository) => _repository = repository;

    public async Task<Unit> Handle(RevokePrivilegedAccessCommand request, CancellationToken cancellationToken)
    {
        var grant = await _repository.GetByIdAsync(request.GrantId, cancellationToken)
            ?? throw new DomainException($"Privileged access grant {request.GrantId} not found.");

        grant.Revoke(request.RevokedByUserId, request.Reason);
        _repository.Update(grant);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return Unit.Value;
    }
}
