#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 Mediator;

namespace Baya.Application.Features.Bookings.Commands.SubmitCareInstructions;

internal sealed class SubmitCareInstructionsCommandHandler(ICurrentUser currentUser, IUnitOfWork unitOfWork)
    : IRequestHandler<SubmitCareInstructionsCommand, OperationResult<CareInstructionsDto>>
{
    private static readonly string[] WritableStatuses =
    [
        BookingStatus.Confirmed, BookingStatus.InProgress,
        BookingStatus.Completed, BookingStatus.Disputed, BookingStatus.Closed
    ];

    public async ValueTask<OperationResult<CareInstructionsDto>> Handle(SubmitCareInstructionsCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<CareInstructionsDto>.UnauthorizedResult("Not authenticated.");

        var booking = await unitOfWork.BookingRepository.GetTrackedWithCareAsync(request.BookingId, cancellationToken);
        if (booking is null)
            return OperationResult<CareInstructionsDto>.NotFoundResult("Booking not found.");

        // Author = the owning customer, or an admin acting on their behalf.
        var isAdmin = currentUser.Roles?.Any(BookingRoles.Admin.Contains) == true;
        var customerId = await unitOfWork.CustomerProfileRepository.GetProfileIdByUserIdAsync(userId, cancellationToken);
        if (!isAdmin && customerId != booking.CustomerId)
            return OperationResult<CareInstructionsDto>.NotFoundResult("Booking not found.");

        if (!WritableStatuses.Contains(booking.Status))
            return OperationResult<CareInstructionsDto>.ConflictResult("Care instructions can only be added once the booking is confirmed.");

        var care = booking.CareInstructions;
        if (care is null)
        {
            care = new BookingCareInstruction { BookingId = booking.Id };
            booking.CareInstructions = care;
        }

        care.CurrentConditions = request.CurrentConditions;
        care.Medications = request.Medications;
        care.Allergies = request.Allergies;
        care.SpecialInstructions = request.SpecialInstructions;
        care.EmergencyContactName = request.EmergencyContactName;
        care.EmergencyContactPhone = request.EmergencyContactPhone;

        await unitOfWork.CommitAsync();

        return OperationResult<CareInstructionsDto>.SuccessResult(new CareInstructionsDto(
            booking.Id,
            care.CurrentConditions,
            care.Medications,
            care.Allergies,
            care.SpecialInstructions,
            care.EmergencyContactName,
            care.EmergencyContactPhone));
    }
}
