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

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

/// <summary>
/// Opens the emergency ticket (assigned nurse or staff) and, when requested, raises an internal
/// <c>support_alerts</c> row via the b1 raise API. The ticket + alert are the durable record staff triage; the
/// emergency call itself is out-of-platform (a <c>tel:</c> link in the UI), so there is no telephony seam here.
/// </summary>
internal sealed class LogEmergencyTicketCommandHandler(
    ICurrentUser currentUser,
    IUnitOfWork unitOfWork,
    IDateTimeProvider dateTimeProvider,
    ISupportAlertService supportAlerts)
    : IRequestHandler<LogEmergencyTicketCommand, OperationResult<OpenTicketResult>>
{
    public async ValueTask<OperationResult<OpenTicketResult>> Handle(LogEmergencyTicketCommand request, CancellationToken cancellationToken)
    {
        if (currentUser.UserId is not { } userId)
            return OperationResult<OpenTicketResult>.UnauthorizedResult("Not authenticated.");

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

        // Only the assigned nurse (or staff) may log an emergency on the booking.
        if (!StaffRoles.IsStaff(currentUser.Roles) && p.NurseUserId != userId)
            return OperationResult<OpenTicketResult>.ForbiddenResult("Only the assigned nurse can log an emergency on this booking.");

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

        var ticket = new Ticket
        {
            ReferenceCode = referenceCode,
            Category = TicketCategory.Emergency,
            Subject = "On-site emergency",
            BookingId = request.BookingId,
            OpenedById = userId
        };
        ticket.Participants.Add(new TicketParticipant
        {
            UserId = userId,
            RoleOnTicket = TicketRoleResolver.Derive(currentUser.Roles),
            AddedById = userId
        });
        if (!string.IsNullOrWhiteSpace(request.Body))
            ticket.Messages.Add(new TicketMessage { SenderId = userId, Body = request.Body, SentAt = now });

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

        // Self-committing facade — runs after the ticket is committed so the alert references a real ticket id.
        if (request.RaiseAlert)
            await supportAlerts.RaiseAsync(
                SupportAlertType.Emergency, entityType: "ticket", entityId: ticket.Id.ToString(),
                severity: SupportAlertSeverity.High, bookingId: request.BookingId, cancellationToken: cancellationToken);

        return OperationResult<OpenTicketResult>.SuccessResult(
            new OpenTicketResult(ticket.Id, ticket.ReferenceCode, ticket.Status, ticket.Category));
    }
}
