<?php
/**
 * EventCapacityService
 *
 * Manages event capacity: availability checks, reservation,
 * release, and waitlist promotion.
 *
 * @package RadiusTheme\RadiusBooking\Services
 * @since   1.2.0
 */

namespace RadiusTheme\RadiusBooking\Services;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use RadiusTheme\RadiusBooking\Repositories\EventRepository;
use RadiusTheme\RadiusBooking\Repositories\EventTicketTypeRepository;
use RadiusTheme\RadiusBooking\Repositories\EventRegistrationRepository;

/**
 * Class EventCapacityService
 */
class EventCapacityService {

	/**
	 * @var EventRepository
	 */
	private EventRepository $event_repo;

	/**
	 * @var EventTicketTypeRepository
	 */
	private EventTicketTypeRepository $ticket_repo;

	/**
	 * @var EventRegistrationRepository
	 */
	private EventRegistrationRepository $registration_repo;

	/**
	 * EventCapacityService constructor.
	 *
	 * @param EventRepository             $event_repo        Event repository.
	 * @param EventTicketTypeRepository   $ticket_repo       Ticket type repository.
	 * @param EventRegistrationRepository $registration_repo Registration repository.
	 */
	public function __construct(
		EventRepository $event_repo,
		EventTicketTypeRepository $ticket_repo,
		EventRegistrationRepository $registration_repo
	) {
		$this->event_repo        = $event_repo;
		$this->ticket_repo       = $ticket_repo;
		$this->registration_repo = $registration_repo;
	}

	/**
	 * Check availability for a ticket type at a given quantity.
	 *
	 * Returns availability info including whether the waitlist is an option.
	 *
	 * @param int $event_id       The event ID.
	 * @param int $ticket_type_id The ticket type ID.
	 * @param int $qty            The requested quantity.
	 *
	 * @return array {
	 *     @type bool $available          Whether tickets are directly available.
	 *     @type int  $remaining          Number of remaining tickets.
	 *     @type bool $waitlist_available  Whether waitlist spots are available.
	 * }
	 */
	public function checkAvailability( int $event_id, int $ticket_type_id, int $qty ): array {
		$event  = $this->event_repo->findOrFail( $event_id );
		$ticket = $this->ticket_repo->findOrFail( $ticket_type_id );

		// Calculate remaining for the ticket type.
		$ticket_remaining = 0 === (int) $ticket->capacity
			? PHP_INT_MAX
			: max( 0, (int) $ticket->capacity - (int) $ticket->quantity_sold );

		// Calculate remaining for overall event capacity.
		$event_remaining = 0 === (int) $event->capacity
			? PHP_INT_MAX
			: max( 0, (int) $event->capacity - (int) $event->registered_count );

		$remaining = min( $ticket_remaining, $event_remaining );
		$available = $remaining >= $qty;

		// Determine waitlist availability.
		$waitlist_available = false;

		if ( ! $available && $event->waitlist_enabled ) {
			$waitlisted_count   = count( $this->registration_repo->getWaitlisted( $event_id ) );
			$waitlist_capacity  = (int) $event->waitlist_capacity;
			$waitlist_available = 0 === $waitlist_capacity || $waitlisted_count < $waitlist_capacity;
		}

		return array(
			'available'          => $available,
			'remaining'          => $remaining === PHP_INT_MAX ? -1 : $remaining,
			'waitlist_available' => $waitlist_available,
		);
	}

	/**
	 * Reserve capacity by incrementing sold/registered counts.
	 *
	 * @param int $event_id       The event ID.
	 * @param int $ticket_type_id The ticket type ID.
	 * @param int $qty            The quantity to reserve.
	 *
	 * @return bool
	 */
	public function reserveCapacity( int $event_id, int $ticket_type_id, int $qty ): bool {
		$this->ticket_repo->incrementSold( $ticket_type_id, $qty );
		$this->event_repo->incrementRegisteredCount( $event_id, $qty );

		return true;
	}

	/**
	 * Release capacity by decrementing sold/registered counts.
	 *
	 * @param int $event_id       The event ID.
	 * @param int $ticket_type_id The ticket type ID.
	 * @param int $qty            The quantity to release.
	 *
	 * @return void
	 */
	public function releaseCapacity( int $event_id, int $ticket_type_id, int $qty ): void {
		$this->ticket_repo->decrementSold( $ticket_type_id, $qty );
		$this->event_repo->decrementRegisteredCount( $event_id, $qty );
	}

	/**
	 * Promote the next waitlisted registration to confirmed.
	 *
	 * Finds the next waitlisted registration, updates its status to confirmed,
	 * reserves capacity, and fires the promotion action hook.
	 *
	 * @param int $event_id The event ID.
	 *
	 * @return void
	 */
	public function promoteFromWaitlist( int $event_id ): void {
		$next = $this->registration_repo->getNextWaitlisted( $event_id );

		if ( ! $next ) {
			return;
		}

		$next->update(
			array(
				'status'            => 'confirmed',
				'waitlist_position' => null,
			)
		);

		$this->reserveCapacity( $event_id, (int) $next->ticket_type_id, (int) $next->quantity );

		/**
		 * Fires after a waitlisted registration is promoted to confirmed.
		 *
		 * @since 1.2.0
		 *
		 * @param object $next     The promoted registration.
		 * @param int    $event_id The event ID.
		 */
		do_action( 'radius_booking_event_waitlist_promoted', $next, $event_id );
	}
}
