<?php
/**
 * REST API v2 Index controller.
 *
 * Provides fast, read-only access to the availability/appointment
 * occurrence index stored in the cache table. This keeps indexing
 * concerns separate from CRUD controllers.
 *
 * @package WooCommerce\Appointments\Rest\Controller\V2
 */

class WC_Appointments_REST_V2_Index_Controller extends WC_REST_Controller {

	/**
	 * Endpoint namespace.
	 *
	 * @var string
	 */
	protected $namespace = WC_Appointments_REST_API::V2_NAMESPACE;

	/**
	 * Route base.
	 *
	 * @var string
	 */
	protected $rest_base = 'index';

	/**
	 * Register routes.
	 */
	public function register_routes(): void {
		register_rest_route(
		    $this->namespace,
		    '/' . $this->rest_base,
		    [
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_items' ],
					'permission_callback' => '__return_true', // Public read.
					'args'                => $this->get_collection_params(),
				],
				'schema' => [ $this, 'get_item_schema' ],
			],
		);
	}

	/**
	 * List indexed occurrences from the cache table.
	 *
	 * Accepts filters that closely match cache columns.
	 */
	public function get_items( $request ): WP_REST_Response {
		$filters = [];

		// Source: 'availability' or 'appointment'. Defaults to 'availability'.
		$source = $request->get_param( 'source' );
		if ( empty( $source ) ) {
			$source = 'availability';
		}
		$filters['source'] = sanitize_text_field( $source );

		// Time window.
		$start_ts = absint( $request->get_param( 'start_ts' ) );
		$end_ts   = absint( $request->get_param( 'end_ts' ) );
		if ( $start_ts && $end_ts && $end_ts > $start_ts ) {
			$filters['time_between'] = [ 'start_ts' => $start_ts, 'end_ts' => $end_ts ];
		}

		// Scope and relations.
		if ( $request->has_param( 'product_id' ) ) {
			$filters['product_id'] = absint( $request->get_param( 'product_id' ) );
		}
		if ( $request->has_param( 'staff_id' ) ) {
			$filters['staff_id'] = absint( $request->get_param( 'staff_id' ) );
		}
		if ( $request->has_param( 'scope' ) ) {
			$filters['scope'] = sanitize_text_field( $request->get_param( 'scope' ) ); // 'global'|'product'|'staff'
		}

		// Appointable filter: accepts boolean-like input; stored as 'yes'/'no'.
		if ( $request->has_param( 'appointable' ) ) {
			$filters['appointable'] = wc_string_to_bool( $request->get_param( 'appointable' ) ) ? 'yes' : 'no';
		}

		// Optional status filter (appointments only).
		if ( $request->has_param( 'status' ) ) {
			$filters['status'] = sanitize_text_field( $request->get_param( 'status' ) );
		}

		// Pagination.
		$per_page = absint( $request->get_param( 'per_page' ) );
		$page     = absint( $request->get_param( 'page' ) );
		if ( 0 < $per_page ) {
			$filters['limit']  = $per_page;
			$filters['offset'] = max( 0, ( 1 < $page ) ? ( $page - 1 ) * $per_page : 0 );
		}

		// Query the cache data store.
		$data_store = WC_Data_Store::load( 'appointments-availability-cache' );
		$rows       = $data_store->get_items( $filters );

		// Expose only rows related to readable (published/visible) products on public endpoints.
		// This keeps parity with WooCommerce Store API, which does not leak draft/private products.
		$rows = array_filter(
		    (array) $rows,
		    function( $row ) {
				if ( ! is_array( $row ) ) {
					return false;
				}
				$pid = absint( $row['product_id'] ?? 0 );
				if ( ! $pid ) {
					return true; // Rows without product association are safe to expose.
				}
				return wc_rest_check_post_permissions( 'product', 'read', $pid );
			},
		);

		return rest_ensure_response( array_values( $rows ) );
	}

	/**
	 * Collection params for documentation.
	 */
	public function get_collection_params(): array {
		$params = parent::get_collection_params();
		$params['source']      = [ 'description' => "Index source ('availability'|'appointment').", 'type' => 'string' ];
		$params['start_ts']    = [ 'description' => 'Start timestamp (UTC).', 'type' => 'integer' ];
		$params['end_ts']      = [ 'description' => 'End timestamp (UTC).', 'type' => 'integer' ];
		$params['product_id']  = [ 'description' => 'Product scope.', 'type' => 'integer' ];
		$params['staff_id']    = [ 'description' => 'Staff scope.', 'type' => 'integer' ];
		$params['scope']       = [ 'description' => "Scope ('global'|'product'|'staff').", 'type' => 'string' ];
		$params['appointable'] = [ 'description' => "Appointable filter (boolean-like; returns 'yes'/'no').", 'type' => 'boolean' ];
		$params['status']      = [ 'description' => 'Appointment status filter.', 'type' => 'string' ];
		return $params;
	}

	/**
	 * JSON Schema for index rows.
	 */
	public function get_item_schema(): array {
		return [
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'appointments_index',
			'type'       => 'object',
			'properties' => [
				'source'       => [ 'type' => 'string' ],
				'source_id'    => [ 'type' => 'integer' ],
				'product_id'   => [ 'type' => 'integer' ],
				'staff_id'     => [ 'type' => 'integer' ],
				'scope'        => [ 'type' => 'string' ],
				'appointable'  => [ 'type' => 'string' ],
				'priority'     => [ 'type' => 'integer' ],
				'ordering'     => [ 'type' => 'integer' ],
				'qty'          => [ 'type' => 'integer' ],
				'start_ts'     => [ 'type' => 'integer' ],
				'end_ts'       => [ 'type' => 'integer' ],
				'range_type'   => [ 'type' => 'string' ],
				'rule_kind'    => [ 'type' => 'string' ],
				'status'       => [ 'type' => 'string' ],
				'date_created' => [ 'type' => 'string' ],
				'date_modified'=> [ 'type' => 'string' ],
			],
		];
	}
}