#nullable enable
using System.Text.Json;
using Baya.Application.Contracts.Common;
using Baya.Application.Contracts.Configuration;
using Baya.Application.Contracts.Persistence;
using Baya.Application.Contracts.SupportAlerts;
using Baya.Application.Models.Booking;
using Baya.Application.Models.Common;
using Baya.Domain.Entities.Booking;
using Baya.Domain.Entities.SupportAlerts;
using Mediator;

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

internal sealed class DetectNoShowSessionsCommandHandler(
    IUnitOfWork unitOfWork,
    IPlatformConfig platformConfig,
    IDateTimeProvider dateTimeProvider,
    ISupportAlertService supportAlerts,
    INotificationDispatcher notifications)
    : IRequestHandler<DetectNoShowSessionsCommand, OperationResult<NoShowSweepResult>>
{
    private const int BatchSize = 200;

    public async ValueTask<OperationResult<NoShowSweepResult>> Handle(DetectNoShowSessionsCommand request, CancellationToken cancellationToken)
    {
        var now = dateTimeProvider.UtcNow.UtcDateTime;
        var thresholdMinutes = await platformConfig.GetConfig<int>("no_show_threshold_minutes", cancellationToken);
        var today = DateOnly.FromDateTime(now);

        var candidates = await unitOfWork.BookingRepository.GetNoShowCandidatesAsync(today, BatchSize, cancellationToken);

        var missed = new List<BookingSession>();
        foreach (var session in candidates)
        {
            var start = session.ScheduledDate.ToDateTime(session.ScheduledTimeStart, DateTimeKind.Utc);
            if (start.AddMinutes(thresholdMinutes) > now)
                continue;

            if (!session.CanTransitionTo(BookingSessionStatus.Missed))
                continue;

            session.TransitionTo(BookingSessionStatus.Missed);
            missed.Add(session);
        }

        if (missed.Count == 0)
            return OperationResult<NoShowSweepResult>.SuccessResult(new NoShowSweepResult(0));

        await unitOfWork.CommitAsync();

        foreach (var session in missed)
        {
            await supportAlerts.RaiseAsync(
                SupportAlertType.EvvNoShow,
                entityType: "booking_session",
                entityId: session.Id.ToString(),
                severity: SupportAlertSeverity.High,
                bookingId: session.BookingId,
                cancellationToken: cancellationToken);

            var participants = await unitOfWork.BookingRepository.GetParticipantsAsync(session.BookingId, cancellationToken);
            if (participants is not null)
                await notifications.DispatchAsync(
                    new Notification(
                        participants.CustomerUserId,
                        "evv_no_show",
                        "Nurse did not check in",
                        "The nurse did not check in for a scheduled visit. Our team has been alerted.",
                        JsonSerializer.Serialize(new { booking_id = session.BookingId, session_id = session.Id })),
                    cancellationToken);
        }

        return OperationResult<NoShowSweepResult>.SuccessResult(new NoShowSweepResult(missed.Count));
    }
}
