<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Callback;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Repositories\Action_Callback_Repository;
use Limb_Chatbot\Includes\Services\Actions\Action_Callback_Service;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for managing Action Callbacks via REST API.
 *
 * Provides endpoints for CRUD operations on action callbacks including:
 * - GET /actions/{action_id}/callbacks - List all callbacks for an action
 * - POST /actions/{action_id}/callbacks - Create a new callback
 * - GET /actions/{action_id}/callbacks/{id} - Get a single callback
 * - PUT /actions/{action_id}/callbacks/{id} - Update an existing callback
 * - DELETE /actions/{action_id}/callbacks/{id} - Delete a callback
 * - DELETE /actions/{action_id}/callbacks - Batch delete multiple callbacks
 * - POST /actions/{action_id}/callbacks/batch - Batch create or update callbacks
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Action_Callbacks_Controller extends Rest_Controller {

	/**
	 * REST route base.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'actions/(?P<action_id>[\d]+)/callbacks';

	/**
	 * Action Callback repository instance.
	 *
	 * @var Action_Callback_Repository
	 * @since 1.0.0
	 */
	protected Action_Callback_Repository $repository;

	/**
	 * Action Callback service instance.
	 *
	 * @var Action_Callback_Service
	 * @since 1.0.0
	 */
	protected Action_Callback_Service $callback_service;

	/**
	 * Action_Callbacks_Controller constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->repository       = new Action_Callback_Repository();
		$this->callback_service = new Action_Callback_Service();
	}

	/**
	 * Registers REST routes for action callbacks.
	 *
	 * Defines routes for CRUD operations and batch operations.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		// GET /callbacks - List all callbacks
		// POST /callbacks - Create a new callback
		// DELETE /callbacks - Batch delete callbacks
		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_action_id_arg(),
			),
			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::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 Action_Callback::count( [ 'id' => $value ] ) === count( $value );
						},
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// GET /callbacks/{id} - Get single callback
		// PUT /callbacks/{id} - Update callback
		// DELETE /callbacks/{id} - Delete single callback
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'required'          => true,
						'validate_callback' => function ( $value ) {
							return ! empty( Action_Callback::find( $value ) );
						},
					),
				),
			),
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'update_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_endpoint_args_for_item_schema(),
			),
			array(
				'methods'             => WP_REST_Server::DELETABLE,
				'callback'            => array( $this, 'delete_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'id' => array(
						'required'          => true,
						'validate_callback' => function ( $value ) {
							return ! empty( Action_Callback::find( $value ) );
						},
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// POST /callbacks/batch - Batch create/update callbacks
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
			array(
				'methods'             => WP_REST_Server::EDITABLE,
				'callback'            => array( $this, 'batch' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'items' => array(
						'type'        => 'array',
						'description' => __( 'Array of callback items to create or update.', 'limb-chatbot' ),
						'required'    => true,
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Returns validation arguments for action_id parameter.
	 *
	 * @return array
	 * @since 1.0.0
	 */
	private function get_action_id_arg(): array {
		return array(
			'action_id' => array(
				'description'       => __( 'Filter callbacks by associated action ID.', 'limb-chatbot' ),
				'type'              => 'integer',
				'required'          => true,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value ) {
					return ! empty( Action::find( $value ) );
				},
			),
		);
	}

	/**
	 * Returns a collection of callback items.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$callbacks = $this->repository->get_items(
				$request->get_param( 'action_id' ),
				$request->get_query_params()
			);
			$items     = $this->prepare_collection( $callbacks, $request );

			return rest_ensure_response( $items ?? [] );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Creates a new callback.
	 *
	 * @param  WP_REST_Request  $request  REST request with callback data.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			$action_id = $request->get_param( 'action_id' );
			$callback  = $this->callback_service->create( $action_id, $request->get_json_params() );

			return rest_ensure_response( $this->prepare_item( $callback, $request ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Prepares a single callback item for REST response.
	 *
	 * @param  mixed  $item  The item to prepare (Action_Callback instance).
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Action_Callback|WP_Error The prepared item or WP_Error if invalid.
	 * @throws \Exception
	 * @since 1.0.0
	 */
	public function prepare_item( $item, $request ) {
		if ( is_a( $item, WP_Error::class ) ) {
			return $item;
		}
		if ( ! is_a( $item, Action_Callback::class ) ) {
			throw new \Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}

		return $item;
	}

	/**
	 * Returns a single callback item by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_item( $request ) {
		try {
			$callback = $this->repository->get_item( $request->get_param( 'id' ) );

			return rest_ensure_response( $this->prepare_item( $callback, $request ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Updates an existing callback by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request with update data.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function update_item( $request ) {
		try {
			$id        = $request->get_param( 'id' );
			$action_id = $request->get_param( 'action_id' );
			$params    = array_merge( [ 'action_id' => $action_id ], $request->get_json_params() );
			$callback  = $this->callback_service->update( $id, $params );

			return rest_ensure_response( $this->prepare_item( $callback, $request ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Deletes a callback by ID.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function delete_item( $request ) {
		try {
			$deleted = $this->repository->delete( $request->get_param( 'id' ) );

			return rest_ensure_response( array( 'deleted' => $deleted ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Batch deletes multiple callbacks by IDs.
	 *
	 * @param  WP_REST_Request  $request  REST request with IDs to delete.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function batch_delete( $request ) {
		try {
			$deleted = $this->repository->batch_delete( $request->get_json_params()['id'] ?? [] );

			return rest_ensure_response( array( 'deleted' => $deleted ) );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

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

	/**
	 * Handles batch create or update of multiple callbacks.
	 *
	 * @param  WP_REST_Request  $request  REST request with batch data.
	 *
	 * @return WP_Error
	 * @since 1.0.0
	 */
	public function batch( $request ) {
		try {
			$items = $request->get_json_params()['items'] ?? [];
			if ( empty( $items ) ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
					__( 'Items array is required for batch operation.', 'limb-chatbot' ) );
			}

			$error = $this->validate_batch_items( $items, $this->get_item_schema()['properties'] );
			if ( is_wp_error( $error ) ) {
				return $error;
			}

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

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

	/**
	 * Returns the JSON schema for an action callback item.
	 *
	 * Defines structure and validation rules for the callback resource.
	 *
	 * @return array JSON schema array.
	 * @since 1.0.0
	 */
	public function get_item_schema() {
		if ( $this->schema ) {
			return $this->schema;
		}

		$this->schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'action_callback',
			'type'       => 'object',
			'properties' => array(
				'id'          => array(
					'description' => __( 'Unique identifier for the callback.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'action_id'   => array(
					'description' => __( 'Associated action ID.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value ) {
							$action = Action::find( $value );
							if ( ! $action instanceof Action ) {
								return false;
							}

							return true;
						},
					),
				),
				'name'        => array(
					'description' => __( 'Unique callback name.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value, $request ) {
							if ( ! is_string( $value ) || empty( $value ) ) {
								return false;
							}
							return true;
						},
					),
				),
				'title'       => array(
					'description' => __( 'Human-readable callback title.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							return is_string( $value ) && ! empty( $value );
						},
					),
				),
				'description' => array(
					'description' => __( 'Callback description.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_textarea_field',
						'validate_callback' => function ( $value ) {
							return is_string( $value );
						},
					),
				),
				'type'        => array(
					'description' => __( 'Callback type.', 'limb-chatbot' ),
					'type'        => 'string',
					'enum'        => array(
						Action_Callback::TYPE_HTTP_REQUEST,
						Action_Callback::TYPE_EMAIL,
						Action_Callback::TYPE_SLACK,
						Action_Callback::TYPE_TRELLO,
						Action_Callback::TYPE_CREATE_POST,
						Action_Callback::TYPE_UPDATE_POST,
						Action_Callback::TYPE_WEBHOOK,
						Action_Callback::TYPE_CUSTOM,
					),
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							$valid_types = array(
								Action_Callback::TYPE_HTTP_REQUEST,
								Action_Callback::TYPE_EMAIL,
								Action_Callback::TYPE_SLACK,
								Action_Callback::TYPE_TRELLO,
								Action_Callback::TYPE_CREATE_POST,
								Action_Callback::TYPE_UPDATE_POST,
								Action_Callback::TYPE_WEBHOOK,
								Action_Callback::TYPE_CUSTOM,
							);

							return in_array( $value, $valid_types, true );
						},
					),
				),
				'config'      => array(
					'description' => __( 'Type-specific callback configuration.', 'limb-chatbot' ),
					'type'        => array( 'object', 'array' ),
					'context'     => array( 'view', 'edit' ),
					'required'    => false,
					'arg_options' => array(
						'validate_callback' => function ( $value ) {
							return is_array( $value );
						},
					),
				),
				'order'       => array(
					'description' => __( 'Execution order of the callback.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'default'     => 0,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value ) {
							return is_numeric( $value );
						},
					),
				),
				'is_required' => array(
					'description' => __( 'Whether callback is required for action to succeed.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'default'     => 1,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value ) {
							return in_array( $value, [ 1, 0 ] );
						},
					),
				),
				'status'      => array(
					'description' => __( 'Callback status (1=active, 0=inactive).', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'default'     => 1,
					'arg_options' => array(
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value ) {
							return in_array( $value, [ 1, 0 ] );
						},
					),
				),
			),
		);

		return $this->schema;
	}
}

