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

namespace IamService.API.Application.Commands.AccessRequests;

/// <summary>
/// EN: Handler for CreateAccessRequestCommand.
/// VI: Handler cho CreateAccessRequestCommand.
/// </summary>
public class CreateAccessRequestCommandHandler : IRequestHandler<CreateAccessRequestCommand, CreateAccessRequestCommandResult>
{
    private readonly IAccessRequestRepository _repository;

    public CreateAccessRequestCommandHandler(IAccessRequestRepository repository)
    {
        _repository = repository;
    }

    public async Task<CreateAccessRequestCommandResult> Handle(
        CreateAccessRequestCommand request,
        CancellationToken cancellationToken)
    {
        var priority = request.Priority.HasValue
            ? AccessRequestPriority.FromId(request.Priority.Value)
            : AccessRequestPriority.Medium;

        var accessRequest = AccessRequest.Create(
            request.RequesterId,
            request.ResourceType,
            request.ResourceId,
            request.RequestedPermission,
            request.Justification,
            priority);

        // EN: Add approvers
        // VI: Thêm approvers
        foreach (var approverId in request.ApproverIds)
        {
            accessRequest.AddApprover(approverId);
        }

        _repository.Add(accessRequest);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return new CreateAccessRequestCommandResult(
            accessRequest.Id,
            accessRequest.Status.Name,
            accessRequest.CreatedAt);
    }
}

/// <summary>
/// EN: Handler for SubmitAccessRequestCommand.
/// VI: Handler cho SubmitAccessRequestCommand.
/// </summary>
public class SubmitAccessRequestCommandHandler : IRequestHandler<SubmitAccessRequestCommand, Unit>
{
    private readonly IAccessRequestRepository _repository;

    public SubmitAccessRequestCommandHandler(IAccessRequestRepository repository)
    {
        _repository = repository;
    }

    public async Task<Unit> Handle(SubmitAccessRequestCommand request, CancellationToken cancellationToken)
    {
        var accessRequest = await _repository.GetByIdWithApproversAsync(request.RequestId, cancellationToken);
        if (accessRequest == null)
            throw new DomainException($"Access request {request.RequestId} not found.");

        accessRequest.Submit();
        _repository.Update(accessRequest);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return Unit.Value;
    }
}

/// <summary>
/// EN: Handler for ApproveAccessRequestCommand.
/// VI: Handler cho ApproveAccessRequestCommand.
/// </summary>
public class ApproveAccessRequestCommandHandler : IRequestHandler<ApproveAccessRequestCommand, Unit>
{
    private readonly IAccessRequestRepository _repository;

    public ApproveAccessRequestCommandHandler(IAccessRequestRepository repository)
    {
        _repository = repository;
    }

    public async Task<Unit> Handle(ApproveAccessRequestCommand request, CancellationToken cancellationToken)
    {
        var accessRequest = await _repository.GetByIdWithApproversAsync(request.RequestId, cancellationToken);
        if (accessRequest == null)
            throw new DomainException($"Access request {request.RequestId} not found.");

        accessRequest.Approve(request.ApproverId, request.Comments);
        _repository.Update(accessRequest);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return Unit.Value;
    }
}

/// <summary>
/// EN: Handler for RejectAccessRequestCommand.
/// VI: Handler cho RejectAccessRequestCommand.
/// </summary>
public class RejectAccessRequestCommandHandler : IRequestHandler<RejectAccessRequestCommand, Unit>
{
    private readonly IAccessRequestRepository _repository;

    public RejectAccessRequestCommandHandler(IAccessRequestRepository repository)
    {
        _repository = repository;
    }

    public async Task<Unit> Handle(RejectAccessRequestCommand request, CancellationToken cancellationToken)
    {
        var accessRequest = await _repository.GetByIdWithApproversAsync(request.RequestId, cancellationToken);
        if (accessRequest == null)
            throw new DomainException($"Access request {request.RequestId} not found.");

        accessRequest.Reject(request.ApproverId, request.Reason);
        _repository.Update(accessRequest);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return Unit.Value;
    }
}

/// <summary>
/// EN: Handler for CancelAccessRequestCommand.
/// VI: Handler cho CancelAccessRequestCommand.
/// </summary>
public class CancelAccessRequestCommandHandler : IRequestHandler<CancelAccessRequestCommand, Unit>
{
    private readonly IAccessRequestRepository _repository;

    public CancelAccessRequestCommandHandler(IAccessRequestRepository repository)
    {
        _repository = repository;
    }

    public async Task<Unit> Handle(CancelAccessRequestCommand request, CancellationToken cancellationToken)
    {
        var accessRequest = await _repository.GetByIdAsync(request.RequestId, cancellationToken);
        if (accessRequest == null)
            throw new DomainException($"Access request {request.RequestId} not found.");

        accessRequest.Cancel();
        _repository.Update(accessRequest);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        return Unit.Value;
    }
}
