<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Data_Schemas;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Repositories\Chatbot_Meta_Repository;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Sanitizer;
use Limb_Chatbot\Includes\Services\Validator;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * REST API controller for managing chatbot meta data.
 *
 * Provides endpoints for creating, retrieving, and batch updating chatbot metas.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Chatbot_Metas_Controller extends Rest_Controller {


	/**
	 * Repository for chatbot meta data.
	 *
	 * @var Chatbot_Meta_Repository
	 * @since 1.0.0
	 */
	protected Chatbot_Meta_Repository $meta_repository;

	/**
	 * Schema definition for chatbot meta data.
	 *
	 * @var Data_Schemas\Chatbot_Meta
	 * @since 1.0.0
	 */
	protected Data_Schemas\Chatbot_Meta $chatbot_meta_schema;

	/**
	 * Sanitizer service for input data.
	 *
	 * @var Sanitizer
	 * @since 1.0.0
	 */
	protected Sanitizer $sanitize;

	/**
	 * Validator service for input data.
	 *
	 * @var Validator
	 * @since 1.0.0
	 */
	protected Validator $validator;

	/**
	 * Chatbot data object.
	 *
	 * @var Chatbot
	 * @since 1.0.0
	 */
	protected Chatbot $chatbot;

	/**
	 * Base REST route for chatbot metas, includes chatbot ID param.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'chatbots/(?P<chatbot_id>[\d]+)/metas';

	/**
	 * Constructor initializes schema, repository, sanitizer, and validator.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->chatbot_meta_schema = new Data_Schemas\Chatbot_Meta();
		$this->meta_repository     = new Chatbot_Meta_Repository();
		$this->sanitize            = new Sanitizer( $this->chatbot_meta_schema );
		$this->validator           = new Validator( $this->chatbot_meta_schema );
	}

	/**
	 * Registers REST API routes for chatbot metas.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			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(),
			),
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge( $this->get_chatbot_id_arg(), $this->get_collection_params() ),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/', array(
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_chatbot_id_arg(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'batch_create_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_chatbot_id_arg(),
			),
			array(
				'methods'             => \WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'batch_create_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_chatbot_id_arg(),
			)
		) );
	}

	/**
	 * Creates a single chatbot meta.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			$data = $request->get_json_params();
			$this->sanitize->sanitize( [ $data['meta_key'] => $data['meta_value'] ], 'create', (bool) $request->get_param( 'preview' ), [ $data['meta_key'] ] );
			$this->validator->validate( $this->sanitize->get_sanitized(), 'create', (bool) $request->get_param( 'preview' ), [ $data['meta_key'] ] );
			if ( $this->validator->has_errors() ) {
				return rest_ensure_response( $this->validator->get_error() );
			}
			$meta = $this->meta_repository->create( [
				'chatbot_id' => $request->get_param( 'chatbot_id' ),
				'meta_key'   => $data['meta_key'],
				'meta_value' => $this->validator->get_validated()[ $data['meta_key'] ]
			] );

			return rest_ensure_response( $meta ?? $error ?? null );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Retrieves a list of chatbot metas.
	 *
	 * @param WP_REST_Request $request REST request.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$params               = $request->get_query_params();
			$params['chatbot_id'] = $request->get_param( 'chatbot_id' );

			return rest_ensure_response( $this->meta_repository->get_items( $params ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Batch create or update chatbot metas.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function batch_create_items( $request ) {
		try {
			$assoc_items = array_column( $request->get_json_params(), 'meta_value', 'meta_key' );
			$meta_keys   = array_keys( $assoc_items );
			$this->sanitize->sanitize( $assoc_items, 'create', (bool) $request->get_param( 'preview' ), $meta_keys );
			$this->validator->validate( $this->sanitize->get_sanitized(), 'create', (bool) $request->get_param( 'preview' ), $meta_keys );
			if ( $this->validator->has_errors() ) {
				return rest_ensure_response( $this->validator->get_error() );
			}

			return rest_ensure_response( $this->meta_repository->batch_update( $this->validator->get_validated(), $request->get_param( 'preview' ) ?? null, $this->chatbot ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Returns the schema for chatbot meta resource.
	 *
	 * @since 1.0.0
	 * @return array
	 */
	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'      => 'Chatbot Meta',
			'type'       => 'object',
			'properties' => array(
				'id'         => array(
					'description'       => __( 'Unique identifier for the resource.', 'limb-chatbot' ),
					'type'              => 'integer',
					'context'           => array( 'view', 'edit' ),
					'readonly'          => true,
					'sanitize_callback' => 'absint',
				),
				'meta_key'   => array(
					'description'       => __( 'Key of the resource', 'limb-chatbot' ),
					'type'              => 'string',
					'required'          => true,
					'sanitize_callback' => 'sanitize_key',
				),
				'meta_value' => array(
					'description' => __( 'Value of the resource', 'limb-chatbot' ),
					'type'        => array( 'string', 'integer', 'array' ),
					'required'    => true,
				)
			),
		);

		return $this->schema;
	}

	/**
	 * Returns collection parameters accepted for queries.
	 *
	 * @since 1.0.0
	 * @return array
	 */
	public function get_collection_params() {
		return array(
			'meta_key' => array(
				'description'       => __( 'Meta keys array', 'limb-chatbot' ),
				'type'              => 'array',
				'validate_callback' => function ( $param, $request, $key ) {
					return is_array( $param );
				}
			)
		);
	}

	/**
	 * Checks permission for the current request.
	 *
	 * @param  WP_REST_Request  $request  REST request.
	 *
	 * @return bool True if current user can manage options.
	 * @since 1.0.0
	 */
	public function permission_callback( $request ): bool {
		if ( ! parent::permission_callback( $request ) ) {
			return false;
		}

		return current_user_can( 'manage_options' );
	}

}