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

namespace Baya.Application.Features.Messaging.Commands.AutoCreateCoordinationTicket;

/// <summary>
/// Creates the one booking-scoped coordination ticket + its nurse/customer participants, idempotently. The
/// idempotency guard is <c>CoordinationTicketExistsForBookingAsync</c> (a re-confirmation is a no-op). Not an
/// end-user path — there is no participant/tenancy gate; the participants are resolved from the booking itself.
/// </summary>
internal sealed class AutoCreateCoordinationTicketCommandHandler(
    IUnitOfWork unitOfWork)
    : IRequestHandler<AutoCreateCoordinationTicketCommand, OperationResult<bool>>
{
    public async ValueTask<OperationResult<bool>> Handle(AutoCreateCoordinationTicketCommand request, CancellationToken cancellationToken)
    {
        if (await unitOfWork.TicketRepository.CoordinationTicketExistsForBookingAsync(request.BookingId, cancellationToken))
            return OperationResult<bool>.SuccessResult(false);

        var parties = await unitOfWork.TicketRepository.GetBookingPartyUserIdsAsync(request.BookingId, cancellationToken);
        if (parties is not { } p)
            return OperationResult<bool>.NotFoundResult("Booking not found.");

        var referenceCode = await TicketReferenceCode.MintAsync(unitOfWork.TicketRepository, cancellationToken);

        var ticket = new Ticket
        {
            ReferenceCode = referenceCode,
            Category = TicketCategory.Coordination,
            Subject = "Booking coordination",
            BookingId = request.BookingId,
            OpenedById = p.CustomerUserId
        };
        ticket.Participants.Add(new TicketParticipant { UserId = p.CustomerUserId, RoleOnTicket = TicketParticipantRole.Customer });
        ticket.Participants.Add(new TicketParticipant { UserId = p.NurseUserId, RoleOnTicket = TicketParticipantRole.Nurse });

        await unitOfWork.TicketRepository.AddAsync(ticket, cancellationToken);

        try
        {
            await unitOfWork.CommitAsync();
        }
        catch (Microsoft.EntityFrameworkCore.DbUpdateException)
        {
            // A concurrent confirmation won the race — the one-per-booking rule holds; treat as no-op.
            await unitOfWork.RollBackAsync();
            return OperationResult<bool>.SuccessResult(false);
        }

        return OperationResult<bool>.SuccessResult(true);
    }
}
