#nullable enable
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Booking;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.User;
using Mediator;

namespace Baya.Application.Features.Booking.Commands.CancelBookingRequest;

internal sealed class CancelBookingRequestCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork)
    : IRequestHandler<CancelBookingRequestCommand, OperationResult<BookingRequestDto>>
{
    public async ValueTask<OperationResult<BookingRequestDto>> Handle(CancelBookingRequestCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<BookingRequestDto>.UnauthorizedResult("Not authenticated.");

        if (currentUser.Roles?.Contains(RoleNames.Customer) != true)
            return OperationResult<BookingRequestDto>.ForbiddenResult("Only a customer can cancel a booking request.");

        var customerId = await unitOfWork.CustomerProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (customerId is not { } cid)
            return OperationResult<BookingRequestDto>.ForbiddenResult("No customer profile exists yet.");

        var bookingRequest = await unitOfWork.BookingRequestRepository.GetTrackedForCustomerAsync(request.Id, cid, cancellationToken);
        if (bookingRequest is null)
            return OperationResult<BookingRequestDto>.NotFoundResult("Booking request not found.");

        if (!bookingRequest.CanTransitionTo(BookingRequestStatus.CancelledByCustomer))
            return OperationResult<BookingRequestDto>.ConflictResult("This request can no longer be cancelled.");

        bookingRequest.CancelByCustomer();
        await unitOfWork.CommitAsync();

        var detail = await unitOfWork.BookingRequestRepository.GetDetailAsync(bookingRequest.Id, cancellationToken);
        // Owning customer — full address.
        return OperationResult<BookingRequestDto>.SuccessResult(BookingRequestMapper.ToDto(detail!, includeFullAddress: true));
    }
}
