<?php
/**
 * MeetingTypeController
 *
 * Handles admin CRUD for meeting types and public booking endpoints.
 * All methods are guarded by rtrb_is_module_active('meeting').
 *
 * @package RadiusTheme\RadiusBooking\Controllers
 * @since   1.2.0
 */

namespace RadiusTheme\RadiusBooking\Controllers;

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

use RadiusTheme\RadiusBooking\Abstracts\BaseController;
use RadiusTheme\RadiusBooking\Core\Api\ApiResponse;
use RadiusTheme\RadiusBooking\Core\Api\Middleware\PermissionMiddleware;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\IntegerRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\RequiredRule;
use RadiusTheme\RadiusBooking\Core\Container\Container;
use RadiusTheme\RadiusBooking\Repositories\MeetingTypeRepository;
use RadiusTheme\RadiusBooking\Resources\MeetingTypeResource;
use RadiusTheme\RadiusBooking\Services\MeetingAvailabilityService;
use RadiusTheme\RadiusBooking\Services\MeetingBookingService;
use RadiusTheme\RadiusBooking\Services\MeetingTypeService;
use WP_REST_Request;

/**
 * Class MeetingTypeController
 */
class MeetingTypeController extends BaseController {

	/**
	 * MeetingTypeController constructor.
	 */
	public function __construct() {
		parent::__construct( Container::resolve( MeetingTypeRepository::class ) );
		$this->middleware[] = new PermissionMiddleware( 'manage_options' );

		add_action( 'radius_booking_item_created_meeting_type', array( $this, 'handleCreated' ), 10, 4 );
		add_action( 'radius_booking_item_updated_meeting_type_with_data', array( $this, 'handleUpdated' ), 10, 4 );
	}

	/**
	 * Transform a single item for API response.
	 *
	 * @param mixed $item The item to transform.
	 *
	 * @return array
	 */
	protected function transformItem( $item ): array {
		return ( new MeetingTypeResource() )->transform( $item );
	}

	/**
	 * Transform a collection for API response.
	 *
	 * @param array $items Items to transform.
	 *
	 * @return array
	 */
	protected function transformCollection( array $items ): array {
		return MeetingTypeResource::collection( $items );
	}

	/**
	 * Get validation rules for store/update.
	 *
	 * @param WP_REST_Request $request The request.
	 * @param string          $context 'create' or 'update'.
	 *
	 * @return array
	 */
	protected function getValidationRules( $request, string $context = 'create' ): array {
		return array(
			'name'        => array( new RequiredRule() ),
			'employee_id' => array( new RequiredRule(), new IntegerRule() ),
			'duration'    => array( new RequiredRule(), new IntegerRule() ),
		);
	}

	/**
	 * Get the resource type identifier.
	 *
	 * @return string
	 */
	protected function getResourceType(): string {
		return 'meeting_type';
	}

	/**
	 * List all meeting types (admin).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function index( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}
		return parent::index( $request );
	}

	/**
	 * Store a new meeting type (admin).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function store( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}
		return parent::store( $request );
	}

	/**
	 * Handle post-creation: sync availability and date overrides.
	 *
	 * Hook signature: do_action('radius_booking_item_created_meeting_type', $data, $item, $request, $controller)
	 *
	 * @param array           $data       The prepared data array.
	 * @param mixed           $item       The created model.
	 * @param WP_REST_Request $request    The original REST request.
	 * @param mixed           $controller The controller instance.
	 *
	 * @return void
	 */
	public function handleCreated( $data, $item, $request, $controller ): void {
		$id = $item->id ?? ( $item['id'] ?? 0 );

		$availability   = $request->get_param( 'availability' );
		$date_overrides = $request->get_param( 'date_overrides' );
		$repo           = Container::resolve( MeetingTypeRepository::class );

		if ( ! empty( $availability ) && is_array( $availability ) ) {
			$repo->syncAvailability( $id, $availability );
		}

		if ( ! empty( $date_overrides ) && is_array( $date_overrides ) ) {
			$repo->syncDateOverrides( $id, $date_overrides );
		}
	}

	/**
	 * Handle post-update: sync availability and date overrides.
	 *
	 * Hook signature: do_action('radius_booking_item_updated_meeting_type_with_data', $data, $item, $request, $controller)
	 *
	 * @param array           $data       The prepared data array.
	 * @param mixed           $item       The updated model.
	 * @param WP_REST_Request $request    The original REST request.
	 * @param mixed           $controller The controller instance.
	 *
	 * @return void
	 */
	public function handleUpdated( $data, $item, $request, $controller ): void {
		$id = $item->id ?? ( $item['id'] ?? 0 );

		$availability   = $request->get_param( 'availability' );
		$date_overrides = $request->get_param( 'date_overrides' );
		$repo           = Container::resolve( MeetingTypeRepository::class );

		if ( null !== $availability && is_array( $availability ) ) {
			$repo->syncAvailability( $id, $availability );
		}

		if ( null !== $date_overrides && is_array( $date_overrides ) ) {
			$repo->syncDateOverrides( $id, $date_overrides );
		}
	}

	/**
	 * Get meeting types by employee (admin).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getByEmployee( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyMiddleware(
			$request,
			function ( $request ) {
				$employee_id = (int) $request->get_param( 'employee_id' );
				$repo        = Container::resolve( MeetingTypeRepository::class );
				$items       = $repo->findByEmployee( $employee_id );

				return ApiResponse::success( $this->transformCollection( $items ) )->send();
			}
		);
	}

	/**
	 * Get available slots for a meeting type on a date (admin).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getAvailableSlots( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyMiddleware(
			$request,
			function ( $request ) {
				$id   = (int) $request->get_param( 'id' );
				$date = sanitize_text_field( $request->get_param( 'date' ) );

				if ( empty( $date ) ) {
					return ApiResponse::validationError(
						array( 'date' => esc_html__( 'Date is required.', 'radius-booking' ) )
					)->send();
				}

				$service = Container::resolve( MeetingAvailabilityService::class );
				$slots   = $service->getAvailableSlots( $id, $date );

				return ApiResponse::success( $slots )->send();
			}
		);
	}

	// ─── Public Endpoints (no auth middleware) ────────────────────────

	/**
	 * Get a meeting type by slug for the public booking page.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getPublicMeetingType( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$slug    = sanitize_text_field( $request->get_param( 'slug' ) );
				$service = Container::resolve( MeetingTypeService::class );
				$result  = $service->getPublicMeetingType( $slug );

				if ( ! $result ) {
					return ApiResponse::notFound( esc_html__( 'Meeting type not found.', 'radius-booking' ) )->send();
				}

				$transformed = ( new MeetingTypeResource() )->transform( $result['meeting_type'] );

				return ApiResponse::success( $transformed )->send();
			}
		);
	}

	/**
	 * Get available slots for a public meeting booking page.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getPublicAvailableSlots( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$slug = sanitize_text_field( $request->get_param( 'slug' ) );
				$date = sanitize_text_field( $request->get_param( 'date' ) );

				$repo = Container::resolve( MeetingTypeRepository::class );
				$mt   = $repo->findBySlug( $slug );

				if ( ! $mt ) {
					return ApiResponse::notFound( esc_html__( 'Meeting type not found.', 'radius-booking' ) )->send();
				}

				if ( empty( $date ) ) {
					// Return available dates instead.
					$service = Container::resolve( MeetingAvailabilityService::class );
					$dates   = $service->getAvailableDates( $mt->id );

					return ApiResponse::success( array( 'available_dates' => $dates ) )->send();
				}

				$service = Container::resolve( MeetingAvailabilityService::class );
				$slots   = $service->getAvailableSlots( $mt->id, $date );

				return ApiResponse::success( array( 'slots' => $slots ) )->send();
			}
		);
	}

	/**
	 * Book a meeting slot (public endpoint).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function bookMeeting( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$slug = sanitize_text_field( $request->get_param( 'slug' ) );
				$data = $request->get_params();

				try {
					$service = Container::resolve( MeetingBookingService::class );
					$result  = $service->bookMeeting( $slug, $data );

					return ApiResponse::created( $result, esc_html__( 'Meeting booked successfully.', 'radius-booking' ) )->send();
				} catch ( \Exception $e ) {
					return ApiResponse::error( $e->getMessage(), 400 )->send();
				}
			}
		);
	}

	/**
	 * Get a meeting booking by its customer-booking token for the public reschedule flow.
	 *
	 * Returns the booking's current date/time, meeting type, and available dates
	 * (excluding the current appointment's own slot from the blocked set).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getBookingByToken( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$token = sanitize_text_field( $request->get_param( 'token' ) );

				$service = Container::resolve( MeetingBookingService::class );
				$context = $service->getBookingByToken( $token );

				if ( ! $context ) {
					return ApiResponse::notFound( esc_html__( 'Booking not found.', 'radius-booking' ) )->send();
				}

				$availability = Container::resolve( MeetingAvailabilityService::class );
				$dates        = $availability->getAvailableDates( (int) $context['meeting_type']->id );

				$response = array(
					'booking'         => $context['booking'],
					'meeting_type'    => ( new MeetingTypeResource() )->transform( $context['meeting_type'] ),
					'available_dates' => $dates,
				);

				return ApiResponse::success( $response )->send();
			}
		);
	}

	/**
	 * Get available slots for a token-based reschedule (excludes the booking's own appointment).
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function getRescheduleSlots( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$token = sanitize_text_field( $request->get_param( 'token' ) );
				$date  = sanitize_text_field( $request->get_param( 'date' ) );

				$service = Container::resolve( MeetingBookingService::class );
				$context = $service->getBookingByToken( $token );

				if ( ! $context ) {
					return ApiResponse::notFound( esc_html__( 'Booking not found.', 'radius-booking' ) )->send();
				}

				$availability = Container::resolve( MeetingAvailabilityService::class );

				if ( empty( $date ) ) {
					$dates = $availability->getAvailableDates( (int) $context['meeting_type']->id );
					return ApiResponse::success( array( 'available_dates' => $dates ) )->send();
				}

				$slots = $availability->getAvailableSlots(
					(int) $context['meeting_type']->id,
					$date,
					(int) $context['appointment']->get_id()
				);

				return ApiResponse::success( array( 'slots' => $slots ) )->send();
			}
		);
	}

	/**
	 * Reschedule a meeting booking via token.
	 *
	 * @param WP_REST_Request $request The request.
	 *
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function rescheduleBooking( WP_REST_Request $request ) {
		if ( ! rtrb_is_module_active( 'meeting' ) ) {
			return ApiResponse::error( esc_html__( 'Meeting module is not active.', 'radius-booking' ), 403 )->send();
		}

		return $this->applyBypass(
			$request,
			function ( $request ) {
				$token = sanitize_text_field( $request->get_param( 'token' ) );
				$data  = $request->get_params();

				try {
					$service = Container::resolve( MeetingBookingService::class );
					$result  = $service->rescheduleBooking( $token, $data );

					return ApiResponse::success( $result, esc_html__( 'Meeting rescheduled successfully.', 'radius-booking' ) )->send();
				} catch ( \Exception $e ) {
					return ApiResponse::error( $e->getMessage(), 400 )->send();
				}
			}
		);
	}
}
