<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Frontend\Page_Context;
use Limb_Chatbot\Includes\Repositories\Chatbot_Repository;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Data_Object_Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\User_Manager;
use Limb_Chatbot\Includes\Utilities\Chatbot_Utility;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for managing Chatbots via REST API.
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Chatbots_Controller extends Rest_Controller {

	/**
	 * REST route base.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	protected $rest_base = 'chatbots';

	/**
	 * Chatbot repository instance.
	 *
	 * @var Chatbot_Repository
	 * @since 1.0.0
	 */
	protected Chatbot_Repository $chatbot_repository;

	/**
	 * Chatbots_Controller constructor.
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->chatbot_repository = new Chatbot_Repository();
	}

	/**
	 * Registers REST routes.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	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, 'get_items_permissions_check' ),
				'args'                => $this->get_collection_params(),
			),
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<chatbot_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})', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_item' ),
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
				'args'                => array_merge( array(
					'chatbot_uuid' => array(
						'validate_callback' => function ( $value ) {
							return ! empty( Chatbot::exists_by_uuid( $value ) );
						}
					)
				), $this->get_collection_params() )
			),
		) );
	}

	/**
	 * Returns a single chatbot 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 {
			$chatbot = $this->chatbot_repository->get_item( $request->get_param( 'chatbot_uuid' ), $request->get_query_params() );

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

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

	/**
	 * Returns a collection of chatbot 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 {
			$chatbots = $this->chatbot_repository->get_items( $request->get_query_params() );
			$items = $this->prepare_collection( $chatbots, $request );

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

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

	/**
	 * Prepares a single chatbot item for REST response.
	 *
	 * @param  mixed  $item  The item to prepare. Expected to be an instance of Chatbot.
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Chatbot|WP_Error The prepared item or WP_Error if item is invalid.
	 *
	 * @throws Exception If the provided item is not an instance of Chatbot.
	 * @since 1.0.0
	 */
	public function prepare_item( $item, $request ) {
		if ( is_a( $item, WP_Error::class ) ) {
			return $item;
		}
		if ( ! is_a( $item, Chatbot::class ) ) {
			throw new Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}
		$include = $request->get_param( 'include' );
		if ( ! empty( $include ) && is_array( $include ) ) {
			foreach ( $include as $relation ) {
				if ( method_exists( $item, $relation ) ) {
					$item->included[ $relation ] = $item->{$relation}();
				}
			}
		}

		// Extra manipulations can be made here
		return $item;
	}

	/**
	 * Returns the query parameters for the chatbot collection endpoint.
	 *
	 * These parameters allow filtering and sorting of chatbot results based on UUID,
	 * included relationships, status, or ordering preferences.
	 *
	 * @return array Array of query parameters accepted by the collection route.
	 * @since 1.0.0
	 */
	public function get_collection_params() {
		return array_merge( parent::get_collection_params(), array(
			'uuid'    => array(
				'description' => __( 'Unique identifier of the Chatbot', 'limb-chatbot' ),
				'type'        => 'array'
			),
			'include' => array(
				'description' => __( 'Include extra data with chatbot.', 'limb-chatbot' ),
				'type'        => 'array'
			),
			'status'  => array(
				'description' => __( 'Sort the collection by status.', 'limb-chatbot' ),
				'type'        => 'integer',
				'enum'        => Chatbot::get_statuses()
			),
			'orderby' => array(
				'description' => __( 'Sort the collection by attribute.', 'limb-chatbot' ),
				'type'        => 'string',
				'default'     => 'date',
				'enum'        => array( 'date', 'ID' )
			)
		) );
	}
	/**
	 * Prepares a collection of chatbot items for REST response.
	 *
	 * @param  Collection|array  $collection  The collection of chatbot objects to prepare.
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Collection The prepared collection with optional enhancements.
	 * @since 1.0.0
	 */
	public function prepare_collection( $collection, $request ) {
		$collection = parent::prepare_collection( $collection, $request );

		// Get page context from headers if available
		$page_context = $this->get_page_context_from_request( $request );

		// Filter CPT-based chatbots based on page context
		if ( $page_context ) {
			$filtered_items = [];
			foreach ( $collection->get() as $chatbot ) {
				if ( $this->should_show_chatbot( $chatbot, $page_context ) ) {
					$filtered_items[] = $chatbot;
				}
			}
			$collection = new Data_Object_Collection( $filtered_items );
		}

		// Handle default chatbot
		if ( ! empty( $request->get_param( 'include' ) ) && in_array( 'default', $request->get_param( 'include' ) ) ) {
			$default_chatbot = Chatbot::make();
			if ( ( empty( $request->get_param( 'status' ) ) || $request->get_param( 'status' ) == $default_chatbot->status ) ) {
				// Check if default chatbot should be shown based on page context
				if ( ! $page_context || $this->should_show_chatbot( $default_chatbot, $page_context ) ) {
					if ( in_array( 'utility', $request->get_param( 'include' ) ) ) {
						$default_chatbot->included['utility'] = new Chatbot_Utility( $default_chatbot );
					}
					$collection->push_property( '_default', $default_chatbot );
				}
			}
			$collection->push_property('_captured_state', User_Manager::instance()->get_current_user()->get_meta('captured_state'));
		}

		return apply_filters( 'lbaic_chatbots_collection_results', $collection, $request );
	}

	/**
	 * Extract page context from request headers.
	 *
	 * @param  WP_REST_Request  $request  The REST request object.
	 *
	 * @return array|null Page context array or null if not available.
	 * @since 1.0.0
	 */
	private function get_page_context_from_request( $request ) {
		$context_header = $request->get_header( 'X-LBAIC-Page-Context' );
		if ( empty( $context_header ) ) {
			return null;
		}

		$context = json_decode( $context_header, true );
		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return null;
		}
		if ( empty( $context['identifier'] ) ) {
			return null;
		}

		return $context;
	}

	/**
	 * Check if a chatbot should be shown based on page context.
	 *
	 * @param  Chatbot  $chatbot  The chatbot object to check.
	 * @param  array  $context  The page context array.
	 *
	 * @return bool True if chatbot should be shown, false otherwise.
	 * @since 1.0.0
	 */
	private function should_show_chatbot( $chatbot, $context ) {
		$show_in = $chatbot->get_parameter('show_in') ?? ['all'];
		$hide_in = $chatbot->get_parameter('hide_in') ?? [];

		// Use Page_Context helper to determine visibility
		return Page_Context::should_show_chatbot( $show_in, $hide_in, $context );
	}

	/**
	 * Checks whether the current user has permission to list chatbot items.
	 *
	 * @param  WP_REST_Request  $request  The REST request being handled.
	 *
	 * @return bool True if the user has permission, false otherwise.
	 * @since 1.0.0
	 */
	public function get_items_permissions_check( $request ) {
		if ( $this->permission_callback( $request ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Checks whether the current user has permission to view a single chatbot item.
	 *
	 * @param  WP_REST_Request  $request  The REST request being handled.
	 *
	 * @return bool True if the user has permission, false otherwise.
	 * @since 1.0.0
	 */
	public function get_item_permissions_check( $request ) {
		if ( $this->permission_callback( $request ) ) {
			return true;
		}

		return false;
	}
}