<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Submission;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Repositories\Action_Submission_Repository;
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 Submissions via REST API.
 *
 * Provides endpoints for retrieving action submission records:
 * - GET /actions/{action_id}/submissions - List all submissions for an action
 * - GET /actions/{action_id}/submissions/{id} - Get a single submission
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Action_Submissions_Controller extends Rest_Controller {

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

	/**
	 * Action Submission repository instance.
	 *
	 * @var Action_Submission_Repository
	 * @since 1.0.0
	 */
	protected Action_Submission_Repository $repository;

	/**
	 * Action_Submissions_Controller constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->repository = new Action_Submission_Repository();
	}

	/**
	 * Registers REST routes for action submissions.
	 *
	 * Defines routes for retrieving submission records.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		// GET /actions/{action_id}/submissions - List all submissions for an action
		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(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// GET /actions/{action_id}/submissions/{id} - Get single submission
		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_Submission::find( $value ) );
						},
					),
				),
			),
			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, $request ) {
							return Action_Submission::count(
								[
									'id'        => absint( $value ),
									'action_id' => absint( $request->get_param( 'action_id' ) ),
								]
							) === 1;
						},
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// POST /actions/{action_id}/submissions/batch-delete - Delete many submissions.
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch-delete', array(
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'batch_delete' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(
					'action_id' => $this->get_action_id_arg()['action_id'],
					'entry_ids' => array(
						'type'              => 'array',
						'required'          => true,
						'sanitize_callback' => function ( $value ) {
							if ( ! is_array( $value ) ) {
								return [];
							}

							return array_values( array_unique( array_filter( array_map( 'absint', $value ) ) ) );
						},
						'validate_callback' => function ( $value, $request ) {
							if ( ! is_array( $value ) || empty( $value ) ) {
								return false;
							}

							return Action_Submission::count(
								[
									'id'        => $value,
									'action_id' => absint( $request->get_param( 'action_id' ) ),
								]
							) === count( $value );
						},
					),
				),
			),
		) );
	}

	/**
	 * 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 submissions by associated action ID.', 'limb-chatbot' ),
				'type'              => 'integer',
				'required'          => true,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value ) {
					return ! empty( Action::find( $value ) );
				},
			),
			'per_page'  => array(
				'description'       => __( 'Maximum number of submissions to return per page.', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => 10,
				'sanitize_callback' => 'absint',
			),
			'page'      => array(
				'description'       => __( 'The page number of the results in paginated set.', 'limb-chatbot' ),
				'type'              => 'integer',
				'default'           => 1,
				'sanitize_callback' => 'absint',
			),
			'orderby'   => array(
				'description'       => __( 'Sort collection by field.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'created_at',
				'enum'              => [ 'id', 'status', 'created_at' ],
				'sanitize_callback' => 'sanitize_key',
			),
			'order'     => array(
				'description'       => __( 'Order sort attribute ascending or descending.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'DESC',
				'enum'              => [ 'ASC', 'DESC' ],
				'sanitize_callback' => 'sanitize_key',
			),
			'search'    => array(
				'description'       => __( 'Limit results to those matching a string.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
		);
	}

	/**
	 * Returns a collection of action submission items for a specific action.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$action_id = $request->get_param( 'action_id' );

			// Get submissions using repository with search and pagination support
			$items = $this->repository->get_items( $action_id, $request->get_query_params() );

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

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

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

			$submission = Action_Submission::find( $id );

			if ( ! $submission ) {
				throw new Exception(
					__( 'Action submission not found', 'limb-chatbot' ),
					Error_Codes::NOT_FOUND
				);
			}

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

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

	/**
	 * Deletes a single action submission.
	 *
	 * @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(
				absint( $request->get_param( 'action_id' ) ),
				absint( $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 action submissions.
	 *
	 * @param  WP_REST_Request  $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function batch_delete( $request ) {
		try {
			$deleted = $this->repository->batch_delete(
				absint( $request->get_param( 'action_id' ) ),
				$request->get_param( 'entry_ids' ) ?? []
			);

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

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

	/**
	 * Prepare submission collection and enrich each item.
	 *
	 * @param  \Limb_Chatbot\Includes\Services\Collection  $collection
	 * @param  WP_REST_Request  $request
	 *
	 * @return \Limb_Chatbot\Includes\Services\Collection
	 */
	public function prepare_collection( $collection, $request ) {
		$collection = parent::prepare_collection( $collection, $request );
		if ( $collection->is_empty() ) {
			return $collection;
		}

		foreach ( $collection->get() as $submission ) {
			$this->prepare_item( $submission, $request );
		}

		return $collection;
	}

	/**
	 * Prepare a submission with parsed attachment metadata.
	 *
	 * @param  mixed  $item
	 * @param  WP_REST_Request  $request
	 *
	 * @return Action_Submission|WP_Error
	 * @throws \Exception
	 */
	public function prepare_item( $item, $request ) {
		if ( is_a( $item, WP_Error::class ) ) {
			return $item;
		}
		if ( ! is_a( $item, Action_Submission::class ) ) {
			throw new \Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}

		$action_data = $item->get_action_data();
		if ( empty( $action_data ) || ! is_array( $action_data ) ) {
			return $item;
		}

		$attachments = [];
		$parameters  = Parameter::where( [ 'action_id' => (int) $item->get_action_id() ] );
		foreach ( $parameters as $parameter ) {
			if ( ! $parameter instanceof Parameter || $parameter->get_type() !== Parameter::TYPE_ATTACHMENT ) {
				continue;
			}

			$key = $parameter->get_name();
			if ( empty( $action_data[ $key ] ) || ! is_string( $action_data[ $key ] ) ) {
				continue;
			}

			$decoded = json_decode( $action_data[ $key ], true );
			if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
				$attachments[ $key ] = $decoded;
			}
		}

		if ( ! empty( $attachments ) ) {
			$item->included['attachments'] = $attachments;
		}

		return $item;
	}

	/**
	 * Get schema for the item.
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public function get_item_schema() {
		return [
			'type'       => 'object',
			'properties' => [
				'id'               => [
					'type'        => 'integer',
					'description' => __( 'Submission ID', 'limb-chatbot' ),
				],
				'action_id'        => [
					'type'        => 'integer',
					'description' => __( 'Associated action ID', 'limb-chatbot' ),
				],
				'action_data'      => [
					'type'        => 'object',
					'description' => __( 'Collected action data', 'limb-chatbot' ),
				],
				'chatbot_user_id'  => [
					'type'        => 'integer',
					'description' => __( 'Chatbot user who submitted', 'limb-chatbot' ),
				],
				'chat_uuid'        => [
					'type'        => 'string',
					'description' => __( 'Associated chat UUID', 'limb-chatbot' ),
				],
				'callback_results' => [
					'type'        => 'object',
					'description' => __( 'Results from executed callbacks', 'limb-chatbot' ),
				],
				'extracted_vars'   => [
					'type'        => 'object',
					'description' => __( 'Variables extracted from callbacks', 'limb-chatbot' ),
				],
				'status'           => [
					'type'        => 'string',
					'enum'        => [ 'pending', 'success', 'failed' ],
					'description' => __( 'Submission status', 'limb-chatbot' ),
				],
				'error_message'    => [
					'type'        => 'string',
					'description' => __( 'Error message if submission failed', 'limb-chatbot' ),
				],
				'created_at'       => [
					'type'        => 'string',
					'format'      => 'date-time',
					'description' => __( 'When the submission was created', 'limb-chatbot' ),
				],
			],
		];
	}
}
