<?php
/**
 * EventService
 *
 * Handles CRUD orchestration for events including
 * ticket type and session synchronization.
 *
 * @package RadiusTheme\RadiusBooking\Services
 * @since   1.2.0
 */

namespace RadiusTheme\RadiusBooking\Services;

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

use RadiusTheme\RadiusBooking\Models\Event;
use RadiusTheme\RadiusBooking\Models\EventTicketType;
use RadiusTheme\RadiusBooking\Models\EventSession;
use RadiusTheme\RadiusBooking\Helpers\SettingsHelper;
use RadiusTheme\RadiusBooking\Repositories\EventRepository;
use RadiusTheme\RadiusBooking\Repositories\EventTicketTypeRepository;
use RadiusTheme\RadiusBooking\Resources\EventResource;

/**
 * Class EventService
 */
class EventService {

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

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

	/**
	 * EventService constructor.
	 *
	 * @param EventRepository           $repo        Event repository.
	 * @param EventTicketTypeRepository $ticket_repo Ticket type repository.
	 */
	public function __construct( EventRepository $repo, EventTicketTypeRepository $ticket_repo ) {
		$this->repo        = $repo;
		$this->ticket_repo = $ticket_repo;
	}

	/**
	 * Create an event with ticket types and sessions.
	 *
	 * @param array $data Event data including optional 'ticket_types' and 'sessions'.
	 *
	 * @return Event The newly created event.
	 */
	public function createEvent( array $data ): Event {
		$ticket_types = $data['ticket_types'] ?? array();
		$sessions     = $data['sessions'] ?? array();
		unset( $data['ticket_types'], $data['sessions'] );

		$event = $this->repo->create( $data );

		if ( ! empty( $ticket_types ) ) {
			$this->syncTicketTypes( $event->id, $ticket_types );
		}

		if ( ! empty( $sessions ) ) {
			$this->syncSessions( $event->id, $sessions );
		}

		return $event;
	}

	/**
	 * Update an event with ticket types and sessions.
	 *
	 * @param int   $id   Event ID.
	 * @param array $data Updated data.
	 *
	 * @return bool Whether the update succeeded.
	 */
	public function updateEvent( int $id, array $data ): bool {
		$ticket_types = $data['ticket_types'] ?? null;
		$sessions     = $data['sessions'] ?? null;
		unset( $data['ticket_types'], $data['sessions'] );

		$result = $this->repo->update( $id, $data );

		if ( null !== $ticket_types ) {
			$this->syncTicketTypes( $id, $ticket_types );
		}

		if ( null !== $sessions ) {
			$this->syncSessions( $id, $sessions );
		}

		return $result;
	}

	/**
	 * Publish an event by setting its status to 'published'.
	 *
	 * @param int $id Event ID.
	 *
	 * @return Event|null The updated event, or null if it could not be found.
	 *
	 * @throws \Exception When the update fails.
	 */
	public function publishEvent( int $id ): ?Event {
		$updated = $this->repo->update( $id, array( 'status' => 'published' ) );
		if ( ! $updated ) {
			throw new \Exception( esc_html__( 'Failed to publish event.', 'radius-booking' ) );
		}
		return $this->repo->find( $id );
	}

	/**
	 * Cancel an event by setting its status to 'cancelled'.
	 *
	 * @param int $id Event ID.
	 *
	 * @return Event|null The updated event, or null if it could not be found.
	 *
	 * @throws \Exception When the update fails.
	 */
	public function cancelEvent( int $id ): ?Event {
		$updated = $this->repo->update( $id, array( 'status' => 'cancelled' ) );
		if ( ! $updated ) {
			throw new \Exception( esc_html__( 'Failed to cancel event.', 'radius-booking' ) );
		}
		return $this->repo->find( $id );
	}

	/**
	 * Get a single event by slug for the public page, with ticket types and sessions.
	 *
	 * @param string $slug The event slug.
	 *
	 * @return array|null Event data with ticket types and sessions, or null if not found.
	 */
	public function getPublicEvent( string $slug ): ?array {
		$event = $this->repo->findBySlug( $slug );

		if ( ! $event ) {
			return null;
		}

		$ticket_types = $this->ticket_repo->getActive( $event->id );
		$sessions     = EventSession::where( 'event_id', $event->id )
			->orderBy( 'sort_order', 'ASC' )
			->get();

		$transformed = ( new EventResource() )->transform( $event );

		$transformed['payment_methods'] = $this->getAvailablePaymentMethods();
		$transformed['currency_symbol'] = function_exists( 'get_rtrb_currency_symbol' ) ? get_rtrb_currency_symbol() : '$';
		$transformed['currency']        = function_exists( 'get_rtrb_currency' ) ? get_rtrb_currency() : 'USD';

		return $transformed;
	}

	/**
	 * Get paginated published event list with optional filters.
	 *
	 * @param array $filters Filters including 'per_page', 'page', 'category_id', 'event_type', 'search'.
	 *
	 * @return array Paginated event results.
	 */
	public function getPublicEventList( array $filters ) {
		$per_page    = (int) ( $filters['per_page'] ?? 12 );
		$page        = (int) ( $filters['page'] ?? 1 );
		$category_id = $filters['category_id'] ?? null;
		$event_type  = $filters['event_type'] ?? null;
		$search      = $filters['search'] ?? null;

		$query = Event::where( 'status', 'published' );

		if ( $category_id ) {
			$query = $query->where( 'category_id', '=', (int) $category_id );
		}

		if ( $event_type ) {
			$query = $query->where( 'event_type', '=', sanitize_text_field( $event_type ) );
		}

		$search_column = null;
		$search_data   = null;

		if ( $search ) {
			$search_column = 'name';
			$search_data   = sanitize_text_field( $search );
		}

		return $query->paginate( $per_page, $page, 'DESC', 'start_date', $search_column, $search_data );
	}

	/**
	 * Replace all ticket types for an event.
	 *
	 * @param int   $event_id    The event ID.
	 * @param array $ticket_data Array of ticket type data.
	 *
	 * @return void
	 */
	private function syncTicketTypes( int $event_id, array $ticket_data ): void {
		EventTicketType::deleteWhere( array( 'event_id' => $event_id ) );

		foreach ( $ticket_data as $index => $ticket ) {
			$ticket_type = $ticket['ticket_type'] ?? 'paid';
			// Free tickets must always persist with a 0 price so stale paid values
			// can't leak into public displays after a type switch.
			$price = ( 'free' === $ticket_type ) ? 0 : ( $ticket['price'] ?? 0 );

			EventTicketType::create(
				array(
					'event_id'        => $event_id,
					'name'            => $ticket['name'] ?? '',
					'description'     => $ticket['description'] ?? '',
					'ticket_type'     => $ticket_type,
					'price'           => $price,
					'capacity'        => $ticket['capacity'] ?? 0,
					'quantity_sold'   => $ticket['quantity_sold'] ?? 0,
					'sale_start_date' => $ticket['sale_start_date'] ?? null,
					'sale_end_date'   => $ticket['sale_end_date'] ?? null,
					'min_per_order'   => $ticket['min_per_order'] ?? 1,
					'max_per_order'   => $ticket['max_per_order'] ?? 10,
					'is_active'       => $ticket['is_active'] ?? true,
					'sort_order'      => $ticket['sort_order'] ?? $index,
				)
			);
		}
	}

	/**
	 * Replace all sessions for an event.
	 *
	 * @param int   $event_id     The event ID.
	 * @param array $session_data Array of session data.
	 *
	 * @return void
	 */
	private function syncSessions( int $event_id, array $session_data ): void {
		EventSession::deleteWhere( array( 'event_id' => $event_id ) );

		foreach ( $session_data as $index => $session ) {
			EventSession::create(
				array(
					'event_id'          => $event_id,
					'name'              => $session['name'] ?? '',
					'description'       => $session['description'] ?? '',
					'session_date'      => $session['session_date'] ?? null,
					'start_time'        => $session['start_time'] ?? null,
					'end_time'          => $session['end_time'] ?? null,
					'speaker'           => $session['speaker'] ?? '',
					'capacity'          => $session['capacity'] ?? 0,
					'location_override' => $session['location_override'] ?? '',
					'sort_order'        => $session['sort_order'] ?? $index,
				)
			);
		}
	}

	/**
	 * Get available payment methods based on global settings.
	 *
	 * @return array
	 */
	private function getAvailablePaymentMethods(): array {
		$global_payments = SettingsHelper::get_setting( 'payment' );
		$methods         = array();

		// Offline is always available.
		$methods[] = 'offline';

		// Stripe — requires pro plugin + global config.
		if ( function_exists( 'rtrb_pro_active' ) && rtrb_pro_active() && ! empty( $global_payments['stripe']['enabled'] ) ) {
			$methods[] = 'stripe';
		}

		// PayPal — requires global config.
		if ( ! empty( $global_payments['paypal']['enabled'] ) ) {
			$methods[] = 'paypal';
		}

		return $methods;
	}
}
