<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Data_Objects\Config;
use Limb_Chatbot\Includes\Data_Objects\Vector_Index;
use Limb_Chatbot\Includes\Repositories\Vector_Index_Repository;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Vector_Index_Service;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * REST API Controller for Vector Indexes management.
 *
 * Handles CRUD operations and listing for vector indexes.
 *
 * @since 1.0.0
 */
class Vector_Indexes_Controller extends Rest_Controller {

	/**
	 * Vector Index Service instance.
	 *
	 * @var Vector_Index_Service
	 * @since 1.0.0
	 */
	protected Vector_Index_Service $vector_index_service;

	/**
	 * Vector Index Repository instance.
	 *
	 * @var Vector_Index_Repository
	 * @since 1.0.0
	 */
	protected Vector_Index_Repository $vector_index_repository;

	/**
	 * REST API base route.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'vector_indexes';

	/**
	 * Constructor.
	 *
	 * Initializes repository and service instances.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->vector_index_repository = new Vector_Index_Repository();
		$this->vector_index_service    = new Vector_Index_Service( $this->vector_index_repository );
	}

	/**
	 * Registers REST API routes for vector indexes.
	 *
	 * @since 1.0.0
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			// Here we register the readable endpoint for collections.
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_collection_params(),
			),
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'create_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
			),
			// Register our schema callback.
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
			// Here we register the readable endpoint for collections.
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'description'       => __( 'Vector Index id', 'limb-chatbot' ),
						'type'              => 'int',
						'required'          => true,
						'context'           => array( 'view', 'edit' ),
						'validate_callback' => array( $this, 'validate_id' ),
					)
				),
			),
			array(
				'methods'             => \WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'description'       => __( 'Vector index id', 'limb-chatbot' ),
						'type'              => 'int',
						'required'          => true,
						'context'           => array( 'view', 'edit' ),
						'validate_callback' => array( $this, 'validate_id' ),
					)
				),
			),
			// Register our schema callback.
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Permission callback to check if current user can manage options.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return bool True if allowed, false otherwise.
	 * @since 1.0.0
	 */
	public function permission_callback( $request ): bool {
		if ( ! parent::permission_callback( $request ) ) {
			return false;
		}

		return current_user_can( 'manage_options' );
	}

	/**
	 * Validate that a Vector Index with given ID exists.
	 *
	 * @param  mixed  $value  ID to validate.
	 *
	 * @return bool True if exists, false otherwise.
	 * @since 1.0.0
	 */
	public function validate_id( $value ) {
		return ! empty( Vector_Index::count( [ 'id' => $value ] ) );
	}

	/**
	 * Get a collection of vector indexes.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$collection = $this->vector_index_repository->get_items( $request->get_query_params() );
			$res        = $this->prepare_collection( $collection, $request );

			return rest_ensure_response( $res );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	public function prepare_collection( Collection $collection, WP_REST_Request $request ) {
		if ( $request->has_param( 'post_type' ) || $request->has_param( 'chatbot_uuid' ) ) {
			return $this->set_pagination_params( $collection, $request );
		}

		return parent::prepare_collection( $collection, $request );
	}

	/**
	 * Get a single vector index by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function get_item( $request ) {
		try {
			return rest_ensure_response( $this->vector_index_repository->get_item( $request->get_param( 'id' ), $request->get_query_params() ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Create a new vector index.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			return $this->prepare_item_for_response( $this->vector_index_service->create( $request->get_json_params() ), $request );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Delete a vector index by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function delete_item( $request ) {
		try {
			return $this->prepare_item_for_response( $this->vector_index_service->delete( $request->get_param( 'id' ) ), $request );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}


	/**
	 * Get JSON Schema for vector index item.
	 *
	 * @return array JSON schema.
	 * @since 1.0.0
	 */
	public function get_item_schema() {
		if ( $this->schema ) {
			// Since WordPress 5.3, the schema can be cached in the $schema property.
			return $this->schema;
		}
		$this->schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'vector_index',
			'type'       => 'object',
			'properties' => array(
				'id'           => array(
					'description' => __( 'Unique identifier for the resource.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true
				),
				'name'         => array(
					'description'       => __( 'Index name', 'limb-chatbot' ),
					'type'              => 'string',
					'context'           => array( 'view', 'edit' ),
					'required'          => true,
					'sanitize_callback' => 'sanitize_text_field',
				),
				'description'  => array(
					'description'       => __( 'Index description', 'limb-chatbot' ),
					'type'              => 'string',
					'context'           => array( 'view', 'edit' ),
					'sanitize_callback' => 'sanitize_text_field',
				),
				'vector_db_id' => array(
					'description' => __( 'ID of external Vector Db storage.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true, // Because config_id contains the db behind it
				),
				'config_id'    => array(
					'description'       => __( 'Config keys id to use to connect to external db api.', 'limb-chatbot' ),
					'type'              => 'int',
					'context'           => array( 'view', 'edit' ),
					'sanitize_callback' => 'sanitize_text_field',
					'arg_options'       => array(
						'validate_callback' => function ( $value ) {
							return ! empty( Config::count( [ 'id' => $value ] ) );
						}
					)
				),
				'metas'        => array(
					'description' => __( 'Metas related to the vector index. Different external indexes has own metas', 'limb-chatbot' ),
					'type'        => 'array',
					'context'     => array( 'view', 'edit' ),
					'arg_options' => array(
						'validate_callback' => function ( $metas ) {
							return is_array( $metas );
						}
					)
				)
			),
		);

		return $this->schema;
	}


	/**
	 * Prepare item for REST response.
	 *
	 * @param  mixed  $item  Item to prepare.
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error Response or error.
	 * @since 1.0.0
	 */
	public function prepare_item_for_response( $item, $request ) {
		if ( is_a( $item, WP_Error::class ) ) {
			return $item;
		}

		return rest_ensure_response( $item );
	}

	/**
	 * Get parameters for collection endpoint.
	 *
	 * @return array Array of collection parameters.
	 * @since 1.0.0
	 */
	public function get_collection_params() {
		return parent::get_collection_params() + array(
				'id' => array(
					'description'       => __( 'Vector index identifiers.', 'limb-chatbot' ),
					'type'              => 'array',
					'validate_callback' => function ( $value ) {
						$value = array_map( function ( $item ) {
							return (int) $item;
						}, $value );

						return Vector_Index::count( [ 'id' => $value ] ) === count( $value );
					}
				),
				'post_type' => array(
					'description'       => __( 'Vector index post type', 'limb-chatbot' ),
					'type'              => 'string',
					'sanitize_callback' => 'sanitize_text_field',
					'validate_callback' => function ( $value ) {
						return post_type_exists( $value );
					}
				)
			);
	}
}