<?php

namespace App\Appointment\Service;

use App\Appointment\Entity\Appointment;
use App\Appointment\Repository\AppointmentRepository;
use App\Payment\Entity\Payment;
use App\Payment\Repository\PaymentRepository;

class AppointmentExpiryService
{
    public function __construct(
        private readonly AppointmentRepository $appointmentRepo,
        private readonly PaymentRepository $paymentRepo,
    ) {}

    /**
     * Expire pending bookings whose payment window has lapsed or whose slot
     * time has already passed. Idempotent — safe to run repeatedly.
     *
     * @return int number of appointments expired
     */
    public function expireStale(): int
    {
        $now = time();

        $expired = [];
        foreach ([...$this->appointmentRepo->findPaymentExpired($now), ...$this->appointmentRepo->findExpiredPending($now)] as $appointment) {
            $expired[$appointment->getUuid()] = $appointment;
        }

        // Fetch all pending payments for the expiring appointments in one query
        // instead of one lookup per appointment (was N+1 in this scheduler loop).
        $pendingByAppointment = $this->paymentRepo->findPendingByAppointments(array_values($expired));

        $count = 0;
        foreach ($expired as $appointment) {
            $appointment->transitionTo(Appointment::STATUS_EXPIRED);
            $this->appointmentRepo->save($appointment, false);

            $payment = $pendingByAppointment[$appointment->getId()] ?? null;
            if ($payment !== null) {
                $payment->setStatus(Payment::STATUS_CANCELED);
                $this->paymentRepo->save($payment, false);
            }

            $count++;
        }

        if ($count > 0) {
            $this->appointmentRepo->save(reset($expired)); // flush once
        }

        return $count;
    }
}
