<?php

declare( strict_types=1 );

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

/**
 * Public REST routes for slot availability.
 * GET /fields/{id}/availability?date=YYYY-MM-DD
 * GET /fields/{id}/availability/range?from=YYYY-MM-DD&to=YYYY-MM-DD
 */
class Playard_API_Availability {

	private Playard_Slot  $slot;
	private Playard_Field $field;

	public function __construct( Playard_Slot $slot, Playard_Field $field ) {
		$this->slot  = $slot;
		$this->field = $field;
	}

	public function register_routes(): void {
		$namespace = 'playard/v1';

		register_rest_route( $namespace, '/fields/(?P<id>\d+)/availability', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'get_availability' ],
			// Public: slot availability is public data for the booking calendar.
			'permission_callback' => '__return_true',
			'args'                => [
				'id'   => [ 'required' => true,  'type' => 'integer' ],
				'date' => [ 'required' => true,  'type' => 'string'  ],
			],
		] );

		register_rest_route( $namespace, '/fields/(?P<id>\d+)/availability/range', [
			'methods'             => 'GET',
			'callback'            => [ $this, 'get_availability_range' ],
			// Public: date range availability is public calendar data.
			'permission_callback' => '__return_true',
			'args'                => [
				'id'   => [ 'required' => true, 'type' => 'integer' ],
				'from' => [ 'required' => true, 'type' => 'string'  ],
				'to'   => [ 'required' => true, 'type' => 'string'  ],
			],
		] );
	}

	// ── Single date ──────────────────────────────────────────────────────────────

	public function get_availability( WP_REST_Request $request ): WP_REST_Response {
		$field_id = absint( $request->get_param( 'id' ) );
		$date     = sanitize_text_field( (string) $request->get_param( 'date' ) );

		if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date ) || ! DateTime::createFromFormat( 'Y-m-d', $date ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_date', __( 'Date must be in YYYY-MM-DD format.', 'playard' ) ),
				422
			);
		}

		$field = $this->field->get( $field_id );

		if ( ! $field || $field->status !== 'active' ) {
			return new WP_REST_Response(
				playard_rest_error( 'not_found', __( 'Field not found.', 'playard' ) ),
				404
			);
		}

		$admin   = current_user_can( 'manage_options' );
		$slots   = $this->slot->get_slots_for_date( $field_id, $date, $admin );
		$day_key = strtolower( (string) gmdate( 'D', (int) strtotime( $date . ' 12:00:00' ) ) );

		return new WP_REST_Response(
			playard_rest_success( [
				'field_id' => $field_id,
				'date'     => $date,
				'day'      => $day_key,
				'is_open'  => count( $slots ) > 0,
				'slots'    => $slots,
			] ),
			200
		);
	}

	// ── Date range (calendar view) ────────────────────────────────────────────────

	public function get_availability_range( WP_REST_Request $request ): WP_REST_Response {
		$field_id = absint( $request->get_param( 'id' ) );
		$from     = sanitize_text_field( (string) $request->get_param( 'from' ) );
		$to       = sanitize_text_field( (string) $request->get_param( 'to' ) );

		if ( ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $from ) || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $to ) ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_date', __( 'Dates must be in YYYY-MM-DD format.', 'playard' ) ),
				422
			);
		}

		if ( $to < $from ) {
			return new WP_REST_Response(
				playard_rest_error( 'invalid_range', __( 'End date must be on or after start date.', 'playard' ) ),
				422
			);
		}

		$field = $this->field->get( $field_id );

		if ( ! $field || $field->status !== 'active' ) {
			return new WP_REST_Response(
				playard_rest_error( 'not_found', __( 'Field not found.', 'playard' ) ),
				404
			);
		}

		$facility  = playard_get_facility();
		$open_days = $facility ? json_decode( (string) ( $facility->open_days ?? '[]' ), true ) : [];
		if ( ! is_array( $open_days ) ) {
			$open_days = [];
		}

		$result  = [];
		$current = strtotime( $from );
		$end     = strtotime( $to );
		$limit   = 90;
		$count   = 0;

		while ( $current <= $end && $count < $limit ) {
			$date    = gmdate( 'Y-m-d', $current );
			$day_key = strtolower( (string) gmdate( 'D', $current ) );
			$slots   = $this->slot->get_slots_for_date( $field_id, $date );

			if ( empty( $slots ) ) {
				if ( playard_is_holiday( $date ) ) {
					$result[ $date ] = [ 'status' => 'holiday', 'slots_available' => 0, 'slots_total' => 0 ];
				} elseif ( ! in_array( $day_key, $open_days, true ) ) {
					$result[ $date ] = [ 'status' => 'closed', 'slots_available' => 0, 'slots_total' => 0 ];
				} else {
					$result[ $date ] = [ 'status' => 'full', 'slots_available' => 0, 'slots_total' => 0 ];
				}
			} else {
				$total     = count( $slots );
				$available = count( array_filter( $slots, static fn( $s ) => $s['status'] === 'available' ) );

				if ( $available === 0 ) {
					$status = 'full';
				} elseif ( $available === $total ) {
					$status = 'available';
				} else {
					$status = 'partial';
				}

				$result[ $date ] = [
					'status'          => $status,
					'slots_available' => $available,
					'slots_total'     => $total,
				];
			}

			$current += DAY_IN_SECONDS;
			$count++;
		}

		return new WP_REST_Response(
			playard_rest_success( $result ),
			200
		);
	}
}
