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

namespace IamService.API.Application.Commands.AccessReviews;

public class CreateAccessReviewCommandHandler : IRequestHandler<CreateAccessReviewCommand, Guid>
{
    private readonly IAccessReviewRepository _repository;
    public CreateAccessReviewCommandHandler(IAccessReviewRepository repository) => _repository = repository;

    public async Task<Guid> Handle(CreateAccessReviewCommand request, CancellationToken cancellationToken)
    {
        var review = AccessReview.Create(request.Name, request.Description, request.OwnerId, request.Scope, request.DueDate);
        _repository.Add(review);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return review.Id;
    }
}

public class AddAccessReviewItemCommandHandler : IRequestHandler<AddAccessReviewItemCommand, Guid>
{
    private readonly IAccessReviewRepository _repository;
    public AddAccessReviewItemCommandHandler(IAccessReviewRepository repository) => _repository = repository;

    public async Task<Guid> Handle(AddAccessReviewItemCommand request, CancellationToken cancellationToken)
    {
        var review = await _repository.GetByIdWithItemsAsync(request.ReviewId, cancellationToken)
            ?? throw new DomainException($"Access review {request.ReviewId} not found.");
        var item = review.AddItem(request.UserId, request.ResourceType, request.ResourceId, request.Permission);
        _repository.Update(review);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return item.Id;
    }
}

public class StartAccessReviewCommandHandler : IRequestHandler<StartAccessReviewCommand, Unit>
{
    private readonly IAccessReviewRepository _repository;
    public StartAccessReviewCommandHandler(IAccessReviewRepository repository) => _repository = repository;

    public async Task<Unit> Handle(StartAccessReviewCommand request, CancellationToken cancellationToken)
    {
        var review = await _repository.GetByIdWithItemsAsync(request.ReviewId, cancellationToken)
            ?? throw new DomainException($"Access review {request.ReviewId} not found.");
        review.Start();
        _repository.Update(review);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return Unit.Value;
    }
}

public class ReviewItemCommandHandler : IRequestHandler<ReviewItemCommand, Unit>
{
    private readonly IAccessReviewRepository _repository;
    public ReviewItemCommandHandler(IAccessReviewRepository repository) => _repository = repository;

    public async Task<Unit> Handle(ReviewItemCommand request, CancellationToken cancellationToken)
    {
        var review = await _repository.GetByIdWithItemsAsync(request.ReviewId, cancellationToken)
            ?? throw new DomainException($"Access review {request.ReviewId} not found.");
        review.ReviewItem(request.ItemId, request.ReviewerUserId, request.Certify, request.Comments);
        _repository.Update(review);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return Unit.Value;
    }
}

public class CompleteAccessReviewCommandHandler : IRequestHandler<CompleteAccessReviewCommand, Unit>
{
    private readonly IAccessReviewRepository _repository;
    public CompleteAccessReviewCommandHandler(IAccessReviewRepository repository) => _repository = repository;

    public async Task<Unit> Handle(CompleteAccessReviewCommand request, CancellationToken cancellationToken)
    {
        var review = await _repository.GetByIdWithItemsAsync(request.ReviewId, cancellationToken)
            ?? throw new DomainException($"Access review {request.ReviewId} not found.");
        review.Complete();
        _repository.Update(review);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        return Unit.Value;
    }
}
