<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Dataset;
use Limb_Chatbot\Includes\Data_Objects\Dataset_Entry;
use Limb_Chatbot\Includes\Repositories\Dataset_Entry_Repository;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Dataset_Entry_Service;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Class Dataset_Entries_Controller
 *
 * REST controller for managing dataset entries.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Dataset_Entries_Controller extends Rest_Controller {

	/**
	 * REST endpoint constant.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	const REST_ENDPOINT = 'entries';

	/**
	 * REST base route with dataset ID parameter.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'datasets/(?P<dataset_id>[\d]+)/dataset_entries';

	/**
	 * Generic REST base route for dataset entries.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected string $generic_rest_base = 'dataset_entries';

	/**
	 * Repository instance for dataset entries.
	 *
	 * @var Dataset_Entry_Repository
	 * @since 1.0.0
	 */
	protected Dataset_Entry_Repository $repository;

	/**
	 * Service instance for dataset entries.
	 *
	 * @var Dataset_Entry_Service
	 * @since 1.0.0
	 */
	protected Dataset_Entry_Service $service;

	/**
	 * Dataset_Entries_Controller constructor.
	 *
	 * Initializes the repository instance.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->repository = new Dataset_Entry_Repository();
		$this->service    = new Dataset_Entry_Service( $this->repository );
	}

	/**
	 * Registers REST API routes for dataset entries.
	 *
	 * Defines routes for CRUD operations and batch updates.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
			),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'create_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema()
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'batch_delete' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'type'              => 'array',
						'required'          => true,
						'validate_callback' => function ( $value ) {
							return Dataset_Entry::count( [ 'id' => $value ] ) === count( $value );
						},
					)
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->generic_rest_base . '/(?P<id>[\d]+)', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_item' ),
				'args'                => $this->dataset_entry_args(),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'update_item' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->generic_rest_base . '/batch', array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'batch' ),
				'permission_callback' => array( $this, 'permission_callback' ),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Returns argument definitions for dataset entry validation.
	 *
	 * Validates the 'id' parameter exists.
	 *
	 * @return array Argument definitions for REST validation.
	 * @since 1.0.0
	 */
	private function dataset_entry_args() {
		return array(
			'id' => array(
				'required'          => true,
				'validate_callback' => function ( $param ) {
					return ! empty( Dataset_Entry::find( $param ) );
				}
			)
		);
	}

	/**
	 * Returns JSON schema for a dataset entry.
	 *
	 * Defines structure and validation rules for dataset entry resource.
	 *
	 * @return array JSON schema array.
	 * @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',
			'type'       => 'object',
			'properties' => array(
				'id'         => array(
					'description' => __( 'Unique identifier for the resource.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'dataset_id' => array(
					'description' => __( 'Dataset id.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'arg_options' => array(
						'validate_callback' => function ( $value ) {
							return ! empty( Dataset::find( $value ) );
						},
					),
				),
				'entry'      => array(
					'description' => __( 'Dataset entry.', 'limb-chatbot' ),
					'type'        => array( 'array', 'object' ),
					'context'     => array( 'view', 'edit' ),
				)
			)
		);

		return $this->schema;
	}

	/**
	 * Checks if the current user has permission to access the endpoint.
	 *
	 * Allows only users with 'manage_options' capability.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return bool True if user can access, 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' );
	}

	/**
	 * Retrieves a collection of dataset entries.
	 *
	 * @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 {
			$dataset_entries = $this->prepare_collection( $this->repository->get_items( $request->get_params() ), $request );

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

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

	/**
	 * Creates a new dataset entry.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request with dataset entry data.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			return rest_ensure_response( $this->service->create( $request->get_json_params() ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Retrieves a single dataset entry 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( Dataset_Entry::find( $request->get_param( 'id' ) ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Deletes a dataset entry 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 rest_ensure_response( $this->service->delete( $request->get_param( 'id' ) ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Deletes a dataset entries by IDs.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function batch_delete( $request ) {
		try {
			return rest_ensure_response( $this->service->batch_delete( $request->get_json_params() ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Updates an existing dataset entry by ID.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request with update data.
	 *
	 * @return WP_REST_Response|WP_Error REST response or error.
	 * @since 1.0.0
	 */
	public function update_item( $request ) {
		try {
			return rest_ensure_response( $this->service->update( $request->get_param( 'id' ), $request->get_json_params() ) );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Handles batch updates or creation of multiple dataset entries.
	 *
	 * Validates and processes batch dataset entries.
	 *
	 * @param  WP_REST_Request  $request  Incoming REST request with batch data.
	 *
	 * @return Collection|true|WP_Error REST response or WP_Error on failure.
	 * @since 1.0.0
	 */
	public function batch( $request ) {
		try {
			$items = $request->get_json_params();
			$error = $this->validate_batch_items( $items, $this->get_item_schema()['properties'] );
			if ( is_wp_error( $error ) ) {
				return $error;
			}

			return $this->service->batch( $items );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

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