<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Data_Objects\Chat_Participant;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\WP_Post_Data_Object;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Factories\Chat_Export_Formatter_Factory;
use Limb_Chatbot\Includes\Factories\Chat_Service_Factory;
use Limb_Chatbot\Includes\Factories\Message_Service_Factory;
use Limb_Chatbot\Includes\Repositories\Chat_Repository;
use Limb_Chatbot\Includes\Repositories\Chatbot_Repository;
use Limb_Chatbot\Includes\Services\Export\Chat_Export_Service;
use Limb_Chatbot\Includes\Services\Chat_Service;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Conversation_State_Analyzer;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\Preview_Chat_Lifecycle_Service;
use Limb_Chatbot\Includes\Services\Stream_Event_Service;
use Limb_Chatbot\Includes\Services\User_Manager;
use Limb_Chatbot\Includes\Data_Objects\Message;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * REST Controller for managing chat-related actions.
 *
 * Provides endpoints to create, retrieve, update, delete, and run chat sessions associated with chatbots.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Chats_Controller extends Rest_Controller {

	/**
	 * Indicates whether the default chatbot's chats are being requested.
	 *
	 * @since 1.0.0
	 * @var bool
	 */
	public bool $default_chatbot_chats_requested = false;

	/**
	 * The current chat instance being manipulated (e.g., for running or clearing).
	 *
	 * @since 1.0.0
	 * @var Chat|null
	 */
	public ?Chat $chat = null;

	/**
	 * The generic REST base path for chats (used for list and creation routes).
	 *
	 * @since 1.0.0
	 * @var string
	 */
	protected string $generic_rest_base = 'chats';

	/**
	 * The detailed REST base regex for identifying specific chat resources via UUID.
	 *
	 * @since 1.0.0
	 * @var string
	 */
	protected $rest_base = 'chats/(?P<uuid>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})';

	public function __construct() {
		$this->message_service_factory = new Message_Service_Factory();
	}

	/**
	 * Registers all REST API routes related to Chats.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->generic_rest_base, array(
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'create_item' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge( $this->get_message_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'                => $this->get_collection_params(),
			),
			'schema' => $this->get_item_schema()
		) );
		register_rest_route( $this->namespace, '/' . $this->generic_rest_base . '/export', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'export_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => $this->get_export_args(),
			),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/export', array(
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => array( $this, 'export_item' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_single_export_args(),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			'methods'             => WP_REST_Server::DELETABLE,
			'callback'            => array( $this, 'delete_item' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_uuid_arg()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => array( $this, 'update_item' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => array_merge( $this->get_uuid_arg(), $this->get_update_args() )
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/run', array(
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => array( $this, 'run' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_run_uuid_arg()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/conversation_state', array(
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => array( $this, 'analyze_conversation_state' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_run_uuid_arg()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/clear', array(
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => array( $this, 'clear' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_uuid_arg()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/wc_sync', array(
			'methods'             => WP_REST_Server::EDITABLE,
			'callback'            => array( $this, 'wc_sync' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_run_uuid_arg()
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => array( $this, 'get_item' ),
			'permission_callback' => array( $this, 'permission_callback' ),
			'args'                => $this->get_uuid_arg()
		) );
	}

	/**
	 * Retrieves query parameters used for filtering chat collections.
	 *
	 * @return array Query parameter definitions.
	 * @since 1.0.0
	 */
	public function get_collection_params() {
		return array(
			'chatbot_uuid' => array(
				'type'              => 'array',
				'validate_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return false;
					}
					$count = count( $value );
					if ( in_array( 'null', $value ) ) {
						// Default chatbot's chats requested
						$count = count( $value ) - 1;
						// Set the flag
						$this->default_chatbot_chats_requested = true;
					}
					if ( empty( $value )
					     || ( $count === 0 && ! $this->default_chatbot_chats_requested )
					     || ( $count > 0 && ( Chatbot::count( [ 'uuid' => $value ] ) !== $count && Chatbot::count( [ 'uuid' => $value, 'status' => WP_Post_Data_Object::STATUS_AUTO_DRAFT ] ) !== $count ) ) ) {
						return false;
					}

					return true;
				},
			),
			'_current_tab' => array(
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			)
		);
	}

	/**
	 * Retrieves the JSON Schema definition for a Chat object.
	 *
	 * @since 1.0.0
	 *
	 * @return array The schema definition.
	 */
	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'      => 'Chat',
			'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',
				),
				'uuid'         => array(
					'description' => __( 'Universal unique identifier for the resource', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'uuid',
					'context'     => array( 'view' ),
				),
				'chatbot_uuid' => array(
					'description' => __( 'Universal unique identifier for the chatbot', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'uuid',
					'context'     => array( 'view', 'create' ),
					'arg_options' => array(
						'validate_callback' => function ( $value, $request, $param ) {
							return Chatbot::exists_by_uuid( $value );
						},
						'sanitize_callback' => function ( $value ) {
							return sanitize_text_field( $value );
						}
					)
				),
				'user_id'      => array(
					'description'       => __( 'User identifier', 'limb-chatbot' ),
					'type'              => 'integer',
					'sanitize_callback' => 'absint',
					'context'           => array( 'view' ),
				),
			),
		);

		return $this->schema;
	}

	/**
	 * Returns the validation arguments for UUID route params.
	 *
	 * @return array UUID parameter validation config.
	 * @since 1.0.0
	 */
	protected function get_uuid_arg() {
		return array(
			'uuid' => array(
				'type'              => 'string',
				'format'            => 'uuid',
				'validate_callback' => function ( $value, $request, $param ) {
					$chat = ( new Chat_Repository() )->get_by_uuid( $request->get_url_params()['uuid'] );
					if ( ! ( $chat instanceof Chat ) ) {
						return false;
					}

					return $chat->has_participant( User_Manager::instance()->get_current_user() ) || $request->get_param( '_current_tab' ) == 'transcripts';
				}
			)
		);
	}

	/**
	 * Handles creation of a new Chat.
	 *
	 * @param  WP_REST_Request  $request  REST request instance.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function create_item( $request ) {
		try {
			$data = $request->get_json_params();
			// Sanitize chatbot_uuid if present
			if ( isset( $data['chatbot_uuid'] ) ) {
				$data['chatbot_uuid'] = sanitize_text_field( $data['chatbot_uuid'] );
			}
			$data['status'] = Chat::STATUS_ACTIVE;
			// Sanitize any other string fields in data
			$data = $this->sanitize_chat_data( $data ?? [] );
			$chatbot     = ( new Chatbot_Repository() )->get_by_uuid( $data['chatbot_uuid'] ?? null );
			$chat_service = ( new Chat_Service_Factory() )->make( $chatbot );
			$chat        = $chat_service->create( $data );
			$message_raw = $request->get_param( 'message' );
			$is_preview  = (bool) $request->get_param( 'preview' );

			if ( $is_preview ) {
				if ( ! current_user_can( 'manage_options' ) ) {
					return new WP_Error( 'forbidden', __( 'Preview is allowed for administrators only.', 'limb-chatbot' ), array( 'status' => 403 ) );
				}
				// Mark chat as preview and remove previous preview chats for this chatbot.
				$chat->update_meta( 'preview', true );
				( new Preview_Chat_Lifecycle_Service() )->delete_existing_preview_chats_for_context( $chat );
			}

			// If no message was provided, return chat links immediately.
			if ( empty( $message_raw ) ) {
				return rest_ensure_response( $this->prepare_item_links( $chat ) );
			}
			// Create the message
			$message = $this->message_service_factory->make( $chat )->create( $message_raw, $is_preview, $this->is_stream_waiting($request) );
			if ( $this->is_stream_waiting( $request ) ) {
				Stream_Event_Service::user_message_saved( $message );
			}
			// Handle chatbot conversation flow
			if ( $message instanceof Message ) {
				$service  = new Chat_Service( $chat->get_chatbot() );
				$response = $service->run( $chat, $is_preview );
				if ( $this->is_stream_waiting( $request ) ) {
					Stream_Event_Service::message( $response );
				}
				$response->inject_previous_message( $message );
				$chat = $chat->inject_message( $response );
			}

			return rest_ensure_response( $this->prepare_item_links( $chat ) );
		} catch ( Exception $e ) {
			if ( ! empty( $chat ) && ! empty( $chatbot ) && $chat instanceof Chat && $chatbot instanceof Chatbot ) {
				if ( $this->is_stream_waiting( $request ) ) {
					Stream_Event_Service::chat_deleted( $chat );
				}
				( new Chat_Service_Factory() )->make( $chatbot )->delete( $chat );
			}
			Helper::log( $e, __METHOD__ );
			if ( ! $request->get_param( 'preview' ) && $e instanceof \Limb_Chatbot\Includes\Exceptions\Exception ) {
				// Any other error that should be shown as it is for the user, could be excepted below
				if ( ! $e->is_limitation_error() && ! empty( $request->get_param( 'message' ) ) ) {
					$e = new \Limb_Chatbot\Includes\Exceptions\Exception( $this->get_error_code( $e, $message ?? null ), __( 'Failed to reply', 'limb-chatbot' ) );
				}
			} elseif ( $request->get_param( 'preview' ) && $e instanceof \Limb_Chatbot\Includes\Exceptions\Exception ) {
				if ( $e->get_error_code() === Error_Codes::AUTHENTICATION_API_KEY_MISSING ) {
					$e = new \Limb_Chatbot\Includes\Exceptions\Exception( Error_Codes::AUTHENTICATION_API_KEY_MISSING_PREVIEW, $e->getMessage() );
				}
			}
			$error = Helper::get_wp_error( $e, __( 'Failed to reply', 'limb-chatbot' ) );
			if ( $this->is_stream_waiting( $request ) ) {
				Stream_Event_Service::error( $e->getMessage(), [ 'error_details' => $error ] );
			}

			return rest_ensure_response( $error );
		}
	}

	private function get_error_code(  $e, $message ) {
		if ( $e instanceof \Limb_Chatbot\Includes\Exceptions\Exception ) {
			return $e->get_error_code();
		}
		if ( ! empty( $message ) && empty( $code ) ) {
			return Error_Codes::USER_MESSAGE_FAILED;
		} elseif ( empty( $message ) ) {
			return Error_Codes::AI_RESPONSE_FAILED;
		}

		return $code ?? Error_Codes::AI_RESPONSE_FAILED;
	}


	/**
	 * Handles conversation state update
	 *
	 * @param  WP_REST_Request  $request  REST request instance.
	 *
	 * @return WP_Error|WP_HTTP_Response|WP_REST_Response
	 * @since 1.0.1
	 */
	public function analyze_conversation_state( WP_REST_Request $request ) {
		try {
			$last_message = $this->chat ? $this->chat->get_last_message() : null;
			if ( ! $last_message instanceof Message || $last_message->get_role() !== Message::ROLE_ASSISTANT ) {
				return rest_ensure_response( [ 'success' => true ] );
			}

			$utility  = $this->chat->get_chatbot()->utility();
			$analyzer = new Conversation_State_Analyzer( $utility );
			$analyzer->analyze_turn( $this->chat->get_uuid() );

			return rest_ensure_response( [ 'success' => true ] );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return rest_ensure_response( [ 'success' => false ] );
		}
	}

	/**
	 * Prepares REST `_links` for one or more Chat objects.
	 *
	 * @param  Chat|Chat[]  $chats  One or more chat objects.
	 *
	 * @return Chat|Chat[]
	 * @since 1.0.0
	 */
	protected function prepare_item_links( $chats ) {
		if ( empty( $chats ) ) {
			return $chats;
		}
		if ( $chats instanceof Chat ) {
			$chats  = array( $chats );
			$single = true;
		}
		foreach ( $chats as $chat ) {
			$chat->included['_links'] = array(
				'messages' => array(
					'href' => rest_url( $this->namespace . '/chats/' . $chat->uuid . '/messages' )
				),
			);
		}

		return $single ? $chat : $chats;
	}

	/**
	 * Executes the chatbot logic for the current chat session.
	 *
	 * Runs the associated Chatbot's logic for the selected chat and returns the response.
	 * If the result is a streamed response, the method exits early.
	 *
	 * @param  WP_REST_Request  $request  The REST request instance containing request parameters.
	 *
	 * @return WP_REST_Response|WP_Error REST response containing result or WP_Error on failure.
	 * @since 1.0.0
	 */
	public function run( $request ) {
		try {
			$reload = (bool) $request->get_param( 'reload' );
			$res    = ( new Chat_Service( $this->chat->get_chatbot() ) )->run( $this->chat, (bool) $request->get_param( 'preview'), $reload );
			if ( $this->is_stream_waiting( $request ) ) {
				Stream_Event_Service::message( $res );
			}

			return rest_ensure_response( $res );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );
			if ( ! $request->get_param( 'preview' ) && $e instanceof \Limb_Chatbot\Includes\Exceptions\Exception ) {
				// Any other error that should be shown as it is for the user, could be excepted below
				if ( ! $e->is_limitation_error() ) {
					$e = new \Limb_Chatbot\Includes\Exceptions\Exception( $this->get_error_code( $e, $message ?? null ), __( 'Failed to reply', 'limb-chatbot' ) );
				}
			}
			$error = Helper::get_wp_error( $e, __( 'Failed to reply', 'limb-chatbot' ) );
			if ( $this->is_stream_waiting( $request ) ) {
				Stream_Event_Service::error( $e->getMessage(), [ 'error_details' => $error ] );
			}

			return rest_ensure_response( $error );
		}
	}


	/**
	 * Clears the current chat conversation by removing all associated messages or data.
	 *
	 * @param  WP_REST_Request  $request  The REST request containing the chat UUID.
	 *
	 * @return WP_REST_Response|WP_Error REST response with clear result or WP_Error on failure.
	 * @since 1.0.0
	 */
	public function clear( $request ) {
		try {
			$chat = ( new Chat_Repository() )->get_by_uuid( $request->get_url_params()['uuid'] );
			$res  = ( new Chat_Service_Factory() )->make( $chat->get_chatbot() )->clear( $chat );

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

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

	/**
	 * Synchronize WooCommerce cart content parts for the current chat.
	 *
	 * Delegates the actual sync logic to active integrations.
	 *
	 * @param  WP_REST_Request  $request
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.1.1
	 */
	public function wc_sync( WP_REST_Request $request ) {
		try {
			if ( ! $this->chat instanceof Chat ) {
				$this->chat = ( new Chat_Repository() )->get_by_uuid( $request->get_url_params()['uuid'] );
			}

			if ( ! $this->chat instanceof Chat ) {
				return new WP_Error(
					'lbaic_chat_not_found',
					__( 'Chat not found.', 'limb-chatbot' ),
					[ 'status' => 404 ]
				);
			}

			$response = apply_filters( 'lbaic_chat_wc_sync_response', null, $this->chat, $request );
			if ( null === $response ) {
				return new WP_Error(
					'lbaic_wc_sync_unavailable',
					__( 'WooCommerce sync is not available.', 'limb-chatbot' ),
					[ 'status' => 404 ]
				);
			}

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

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

	/**
	 * Updates an existing chat resource using the provided UUID and request payload.
	 *
	 * @param  WP_REST_Request  $request  The REST request instance containing UUID and update data.
	 *
	 * @return WP_REST_Response|WP_Error REST response on success or WP_Error on failure.
	 * @since 1.0.0
	 */
	public function update_item( $request ) {
		try {
			$data = $request->get_json_params();
			// Sanitize update data
			$data = $this->sanitize_chat_data( $data ?? [] );
			$res = ( new Chat_Repository() )->update( [ 'uuid' => $request->get_url_params()['uuid'] ], $data );

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

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

	public function get_item( $request ) {
		try {
			$uuid = sanitize_text_field( $request->get_param('uuid') );
			$include = $request->get_param('include');
			// Sanitize include if it's an array
			if ( is_array( $include ) ) {
				$include = array_map( 'sanitize_text_field', $include );
			} elseif ( is_string( $include ) ) {
				$include = sanitize_text_field( $include );
			}
			$res = ( new Chat_Repository() )->get_item( $uuid, $include );

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

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

	/**
	 * Deletes a specific chat instance identified by UUID.
	 *
	 * @param  WP_REST_Request  $request  The REST request with the chat UUID in URL parameters.
	 *
	 * @return WP_REST_Response|WP_Error REST response indicating success or WP_Error on failure.
	 * @since 1.0.0
	 */
	public function delete_item( $request ) {
		try {
			$chat = ( new Chat_Repository() )->get_by_uuid( $request->get_url_params()['uuid'] );
			$res  = ( new Chat_Service_Factory() )->make( $chat->get_chatbot() )->delete( $chat );

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

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

	/**
	 * Retrieves a collection of chats based on query parameters such as chatbot UUID.
	 *
	 * Applies personalized filtering logic for non-admin users to only fetch their own data.
	 *
	 * @param  WP_REST_Request  $request  The REST request with optional filter parameters.
	 *
	 * @return WP_REST_Response|WP_Error Collection of chats or error on failure.
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$params     = $request->get_query_params();
			$params     = $this->fill_personalized_params( $params, $request );
			$collection = ( new Chat_Repository() )->get_items( $params );
			$items      = $this->prepare_collection( $collection, $request );

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

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


	/**
	 * Prepares the chat collection and sets the total number of items in the result.
	 *
	 * This method is typically called before returning the response to include meta information.
	 *
	 * @param  Collection  $collection  The collection of chats.
	 * @param  WP_REST_Request  $request  The current REST request.
	 *
	 * @return Collection The updated chat collection with total count.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function prepare_collection( $collection, $request ) {
		parent::prepare_collection( $collection, $request );
		$collection->set_total( Chat::count( $this->fill_personalized_params( $request->get_query_params(), $request ) ) ?? 0 );

		return $collection;
	}

	/**
	 * Applies user-based filtering rules to the given parameter set.
	 *
	 * If the current user is not an admin, the parameters are restricted to their own chatbot UUID.
	 *
	 * @param  array  $params  The original query or body parameters.
	 *
	 * @return array The updated parameters including user-specific filters.
	 * @throws Exception
	 * @since 1.0.0
	 */
	protected function fill_personalized_params( $params, WP_REST_Request $request) {
		// Initialize extracted_chat_ids for proper scope
		$extracted_chat_ids = [];

		// Validate and sanitize input parameters
		if ( ! is_array( $params ) ) {
			$params = [];
		}

		// Apply user-based filtering for non-admin users
		if ( $request->get_param( '_current_tab' ) !== 'transcripts' ) {
			$current_user = User_Manager::instance()->get_current_user();
			if ( $current_user && $current_user->get_id() ) {
				$extracted_chat_ids = Chat_Participant::where( [
					'user_id' => absint( $current_user->get_id() ),
					'role'    => Chat_Participant::ROLE_USER
				] )->pluck( 'chat_id' );
				if ( empty( $extracted_chat_ids ) ) {
					$extracted_chat_ids = [ 0 ];
				} else {
					// Sanitize chat IDs
					$extracted_chat_ids = array_map( 'absint', $extracted_chat_ids );
				}
				$params['id'] = $extracted_chat_ids;
			} else {
				// No valid user, return empty results
				$params['id'] = [ 0 ];
			}
		}

		// Internal flag for repository-level transcript-specific filters.
		$params['_exclude_preview_chats'] = ( $request->get_param( '_current_tab' ) === 'transcripts' );

		// Sanitize and validate date parameters
		if ( ! empty( $params['start_date'] ) ) {
			$start_date = sanitize_text_field( $params['start_date'] );
			if ( ! empty( $start_date ) ) {
				$params['created_at>='] = $start_date;
			}
			unset( $params['start_date'] );
		}

		if ( ! empty( $params['end_date'] ) ) {
			$end_date = sanitize_text_field( $params['end_date'] );
			if ( ! empty( $end_date ) ) {
				$params['created_at<='] = $end_date;
			}
			unset( $params['end_date'] );
		}

		// Handle search parameter with proper sanitization
		if ( ! empty( $params['search'] ) ) {
			$search_term = sanitize_text_field( $params['search'] );
			if ( ! empty( $search_term ) ) {
				$matching_chat_ids = ( new Chat_Repository() )->get_search_matching_chat_ids( $search_term );
				if ( empty( $matching_chat_ids ) ) {
					$params['id'] = [ 0 ];
				} else {
					// Sanitize matching chat IDs
					$matching_chat_ids = array_map( 'absint', $matching_chat_ids );

					// Apply user-based filtering if not admin
					if ( $request->get_param( '_current_tab' ) !== 'transcripts' && ! empty( $extracted_chat_ids ) ) {
						$params['id'] = array_values( array_intersect( $matching_chat_ids, $extracted_chat_ids ) );
						// Ensure we have at least one ID (even if 0 for no results)
						if ( empty( $params['id'] ) ) {
							$params['id'] = [ 0 ];
						}
					} else {
						$params['id'] = $matching_chat_ids;
					}
				}
			} else {
				// Empty search term, return no results
				$params['id'] = [ 0 ];
			}
		}

		return $params;
	}

	/**
	 * Returns an array of arguments used for validating chat update requests.
	 *
	 * Includes sanitation and validation logic for fields like name.
	 *
	 * @return array Associative array of REST argument definitions.
	 * @since 1.0.0
	 */
	private function get_update_args() {
		return array(
			'name' => [
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value ) {
					return strlen( $value ) <= 256 && ! empty( $value );
				},
			]
		);
	}

	/**
	 * Returns the REST route arguments for running a chat, including UUID validation.
	 *
	 * Stores the matching Chat object into the controller's `$chat` property for later use.
	 *
	 * @return array Array of REST parameter definitions used for route validation.
	 * @since 1.0.0
	 */
	private function get_run_uuid_arg() {
		return array(
			'uuid' => array(
				'type'              => 'string',
				'format'            => 'uuid',
				'validate_callback' => function ( $value, $request, $param ) {
					$chat = ( new Chat_Repository() )->get_by_uuid( $request->get_url_params()['uuid'] );
					if ( ! ( $chat instanceof Chat ) ) {
						return false;
					}
					$this->chat = $chat;

					return $chat->has_participant(User_Manager::instance()->get_current_user());
				}
			)
		);
	}

	private function get_message_args() {
		$messages_controller = new Chat_Messages_Controller();
		return array(
			'message' => array(
				'description'       => __( 'Message content', 'limb-chatbot' ),
				'context'           => array( 'view', 'edit' ),
				'validate_callback' => function ( $message ) use ( $messages_controller ) {
					if ( ! empty( $message['content'] ) ) {
						$content_validated = $messages_controller->validate_content( $message['content'] );
						if ( ! $content_validated ) {
							return false;
						}
					} else {
						return false;
					}

					return in_array( $message['role'], Message::get_allowed_roles() );
				},
				'sanitize_callback' => function ( $message ) use ( $messages_controller ) {
					if ( ! empty( $message['content'] ) ) {
						$message['content'] = $messages_controller->sanitize_content( $message['content'] );
					}
					if ( isset( $message['role'] ) ) {
						$message['role'] = sanitize_text_field( $message['role'] );
					}
					if ( isset( $message['user_id'] ) ) {
						$message['user_id'] = $message['user_id'] === null ? null : absint( $message['user_id'] );
					}
					return $message;
				}
			),
			'preview' => array(
				'description'       => __( 'Preview mode', 'limb-chatbot' ),
				'type'              => 'boolean',
				'sanitize_callback' => function ( $value ) {
					return (bool) $value;
				}
			)
		);
	}

	/**
	 * Sanitize chat creation data.
	 *
	 * Sanitizes all string fields in the chat data array.
	 *
	 * @param array $data The chat data to sanitize.
	 * @return array Sanitized chat data.
	 * @since 1.0.0
	 */
	private function sanitize_chat_data( array $data ): array {
		$sanitized = array();
		foreach ( $data as $key => $value ) {
			$sanitized_key = sanitize_key( $key );
			if ( is_string( $value ) ) {
				$sanitized[ $sanitized_key ] = sanitize_text_field( $value );
			} elseif ( is_int( $value ) ) {
				$sanitized[ $sanitized_key ] = absint( $value );
			} elseif ( is_bool( $value ) ) {
				$sanitized[ $sanitized_key ] = (bool) $value;
			} elseif ( is_array( $value ) ) {
				$sanitized[ $sanitized_key ] = $this->sanitize_chat_data( $value );
			} else {
				$sanitized[ $sanitized_key ] = $value;
			}
		}
		return $sanitized;
	}

	/**
	 * Handle the chat export request.
	 *
	 * @param WP_REST_Request $request The incoming request.
	 *
	 * @return void|WP_Error
	 * @since 1.0.0
	 */
	public function export_items( WP_REST_Request $request ) {
		try {
			$params = $this->fill_personalized_params( $request->get_query_params(), $request );

			( new Chat_Export_Service() )->export( $params );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e, __( 'Failed to export chats.', 'limb-chatbot' ) );
		}
	}

	/**
	 * Handle single chat export by UUID.
	 *
	 * Public endpoint — accessible by any user (including guests).
	 * Participant ownership is enforced via validate_callback in get_single_export_args().
	 *
	 * @param WP_REST_Request $request The incoming request.
	 *
	 * @return void|WP_Error
	 * @since 1.0.14
	 */
	public function export_item( WP_REST_Request $request ) {
		try {
			$uuid   = sanitize_text_field( $request->get_url_params()['uuid'] );
			$format = sanitize_text_field( $request->get_param( 'format' ) ?? 'json' );
			$chat   = ( new Chat_Repository() )->get_by_uuid( $uuid );

			if ( ! ( $chat instanceof Chat ) ) {
				return new WP_Error(
					Error_Codes::NOT_FOUND,
					__( 'Chat not found.', 'limb-chatbot' ),
					array( 'status' => 404 )
				);
			}

			( new Chat_Export_Service() )->export_single_chat( $chat, $format );
		} catch ( Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e, __( 'Failed to export chat.', 'limb-chatbot' ) );
		}
	}


	/**
	 * Get single chat export endpoint args — UUID with participant check plus format param.
	 *
	 * The UUID validate_callback resolves the current user via User_Manager
	 * (works for guests via device_uuid cookie / IP) and verifies participant ownership.
	 *
	 * @return array REST argument definitions.
	 * @since 1.0.14
	 */
	private function get_single_export_args(): array {
		return [
			'format' => [
				'description'       => __( 'Export format.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'json',
				'enum'              => Chat_Export_Formatter_Factory::ALLOWED_FORMATS,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			],
		];
	}

	/**
	 * Get export endpoint args — format param plus standard collection params.
	 *
	 * @return array REST argument definitions.
	 * @since 1.0.0
	 */
	private function get_export_args(): array {
		return array_merge( $this->get_collection_params(), [
			'format' => [
				'description'       => __( 'Export format.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'json',
				'enum'              => Chat_Export_Formatter_Factory::ALLOWED_FORMATS,
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			],
		] );
	}
}