<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Chatbot_User;
use Limb_Chatbot\Includes\Repositories\Chatbot_User_Repository;
use Limb_Chatbot\Includes\Services\Collection;
use Limb_Chatbot\Includes\Services\Helper;
use Limb_Chatbot\Includes\Services\User_Manager;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for managing Chatbot Users via REST API.
 *
 * Provides endpoints for retrieving chatbot user records with filtering, search, and pagination:
 * - GET /chatbot-users - List all chatbot users with filtering, search, and pagination
 * - GET /chatbot-users/{id} - Get a single chatbot user by ID
 *
 * @package Limb_Chatbot\Includes\Api\V1\Controllers
 * @since 1.0.0
 */
class Chatbot_Users_Controller extends Rest_Controller {

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

	/**
	 * Chatbot user repository instance.
	 *
	 * @var Chatbot_User_Repository
	 * @since 1.0.0
	 */
	protected Chatbot_User_Repository $repository;

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

	/**
	 * Registers REST routes for chatbot users.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function register_routes() {
		// GET /chatbot-users - List all chatbot users with filtering and search
		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_collection_params(),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );

		// GET /chatbot-users/{id} - Get single chatbot user
		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(
						'description'       => __( 'Unique identifier for the chatbot user.', 'limb-chatbot' ),
						'type'              => 'integer',
						'required'          => true,
						'sanitize_callback' => 'absint',
						'validate_callback' => function ( $value, $request, $param ) {
							if ( ! is_numeric( $value ) || $value < 1 ) {
								return new WP_Error( 'invalid_id',
									__( 'ID must be a positive integer.', 'limb-chatbot' ) );
							}
							if ( empty( Chatbot_User::find( $value ) ) ) {
								return new WP_Error( 'not_found', __( 'Chatbot user not found.', 'limb-chatbot' ) );
							}

							return true;
						},
					),
				),
			),
			'schema' => array( $this, 'get_item_schema' ),
		) );
	}

	/**
	 * Returns query parameters for the collection endpoint.
	 *
	 * These parameters allow filtering, searching, and sorting of chatbot user results.
	 *
	 * @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(
			'orderby'       => array(
				'description'       => __( 'Sort the collection by attribute.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'created_at',
				'enum'              => array( 'id', 'created_at', 'updated_at', 'type', 'status' ),
				'validate_callback' => 'rest_validate_request_arg',
				'sanitize_callback' => 'sanitize_key',
			),
			'order'         => array(
				'description'       => __( 'Order sort attribute ascending or descending.', 'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'desc',
				'enum'              => array( 'asc', 'desc' ),
				'validate_callback' => 'rest_validate_request_arg',
				'sanitize_callback' => 'sanitize_key',
			),
			'search'        => array(
				'description'       => __( 'Search chatbot users by UUID, device UUID, or IP address.',
					'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'search_fields' => array(
				'description'       => __( 'Fields to search in (e.g., uuid, device_uuid, ip).', 'limb-chatbot' ),
				'type'              => 'array',
				'default'           => array( 'uuid', 'device_uuid' ),
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return array();
					}

					return array_map( 'sanitize_key', $value );
				},
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! is_array( $value ) ) {
						return new WP_Error( 'invalid_search_fields',
							__( 'search_fields must be an array.', 'limb-chatbot' ) );
					}
					$allowed_fields = array( 'uuid', 'device_uuid', 'ip' );
					foreach ( $value as $field ) {
						if ( ! in_array( $field, $allowed_fields, true ) ) {
							return new WP_Error( 'invalid_search_field',
								sprintf( __( 'Invalid search field: %s. Allowed fields are: %s', 'limb-chatbot' ),
									$field,
									implode( ', ', $allowed_fields ) ) );
						}
					}

					return true;
				},
			),
			'type'          => array(
				'description'       => __( 'Filter by user type.', 'limb-chatbot' ),
				'type'              => 'integer',
				'enum'              => array(
					Chatbot_User::TYPE_GUEST,
					Chatbot_User::TYPE_WP_USER,
					Chatbot_User::TYPE_AGENT
				),
				'sanitize_callback' => 'absint',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'status'        => array(
				'description'       => __( 'Filter by user lifecycle status (new, interacted, blocked, converted).',
					'limb-chatbot' ),
				'type'              => 'integer',
				'enum'              => array(
					Chatbot_User::STATUS_NEW,
					Chatbot_User::STATUS_INTERACTED,
					Chatbot_User::STATUS_BLOCKED,
					Chatbot_User::STATUS_CONVERTED
				),
				'sanitize_callback' => 'absint',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'wp_user_id'    => array(
				'description'       => __( 'Filter by WordPress user ID.', 'limb-chatbot' ),
				'type'              => 'integer',
				'minimum'           => 1,
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! is_numeric( $value ) || $value < 1 ) {
						return new WP_Error( 'invalid_wp_user_id',
							__( 'wp_user_id must be a positive integer.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
			'uuid'          => array(
				'description'       => __( 'Filter by chatbot user UUID.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'device_uuid'   => array(
				'description'       => __( 'Filter by device UUID.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => 'rest_validate_request_arg',
			),
			'country'       => array(
				'description'       => __( 'Filter users by country from geolocation metadata.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'city'          => array(
				'description'       => __( 'Filter users by city from geolocation metadata.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'start_date'    => array(
				'description'       => __( 'Filter users created on or after this date (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).',
					'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! empty( $value ) && ! strtotime( $value ) ) {
						return new WP_Error( 'invalid_date',
							__( 'Invalid date format. Use YYYY-MM-DD or YYYY-MM-DD HH:MM:SS.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
			'end_date'      => array(
				'description'       => __( 'Filter users created on or before this date (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS).',
					'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! empty( $value ) && ! strtotime( $value ) ) {
						return new WP_Error( 'invalid_date',
							__( 'Invalid date format. Use YYYY-MM-DD or YYYY-MM-DD HH:MM:SS.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
			'created_at>='  => array(
				'description'       => __( 'Filter users created on or after this datetime.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! empty( $value ) && ! strtotime( $value ) ) {
						return new WP_Error( 'invalid_datetime', __( 'Invalid datetime format.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
			'created_at<='  => array(
				'description'       => __( 'Filter users created on or before this datetime.', 'limb-chatbot' ),
				'type'              => 'string',
				'sanitize_callback' => 'sanitize_text_field',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( ! empty( $value ) && ! strtotime( $value ) ) {
						return new WP_Error( 'invalid_datetime', __( 'Invalid datetime format.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
			'include'       => array(
				'description'       => __( 'Include extra data with chatbot users (e.g., name, email, agent, geolocation).',
					'limb-chatbot' ),
				'type'              => 'array',
				'sanitize_callback' => function ( $value ) {
					if ( ! is_array( $value ) ) {
						return array();
					}

					return array_map( 'sanitize_key', $value );
				},
			),
			'online'        => array(
				'description'       => __( 'When 1, return only users with updated_at within the last 2 minutes, excluding the current chatbot user.',
					'limb-chatbot' ),
				'type'              => 'integer',
				'enum'              => array( 0, 1 ),
				'sanitize_callback' => 'absint',
				'validate_callback' => function ( $value, $request, $param ) {
					if ( $value !== null && $value !== '' && ! in_array( (int) $value, array( 0, 1 ), true ) ) {
						return new WP_Error( 'invalid_online',
							__( 'online must be 0 or 1.', 'limb-chatbot' ) );
					}

					return true;
				},
			),
		) );
	}

	/**
	 * Returns a collection of chatbot user items.
	 *
	 * Supports advanced filtering, search, and pagination.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$query_params = $request->get_query_params();
			if ( isset( $query_params['online'] ) && (int) $query_params['online'] === 1 ) {
				$current = User_Manager::instance()->get_current_user();
				if ( $current instanceof Chatbot_User ) {
					$query_params['exclude_chatbot_user_id'] = $current->get_id();
				}
			}
			$users        = $this->repository->get_items( $query_params );
			$items        = $this->prepare_collection( $users, $request );

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

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

	/**
	 * Prepares a collection of chatbot user items for REST response.
	 *
	 * Ensures each user includes type label and optional relations.
	 *
	 * @param  Collection  $collection  The collection of chatbot users.
	 * @param  WP_REST_Request  $request  The REST request object.
	 *
	 * @return Collection Prepared collection with full data.
	 * @throws Exception
	 * @since 1.0.0
	 */
	public function prepare_collection( $collection, $request ) {
		$this->set_pagination_params($collection, $request);

		if ( $collection->is_empty() ) {
			return $collection;
		}

		// Prepare each item with full data
		foreach ( $collection->get() as $user ) {
			$this->prepare_item( $user, $request );
		}

		return $collection;
	}

	/**
	 * Prepares a single chatbot user item for REST response.
	 *
	 * Converts type to label and includes optional relations.
	 *
	 * @param  mixed  $item  The item to prepare (Chatbot_User instance).
	 * @param  WP_REST_Request  $request  The full REST request object.
	 *
	 * @return Chatbot_User|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, Chatbot_User::class ) ) {
			throw new \Exception( __( 'Unknown item type.', 'limb-chatbot' ) );
		}

		// Convert type to label
		$item->included['type_label'] = $item->get_type_label();

		// Handle additional included relations
		$include = $request->get_param( 'include' );
		if ( ! empty( $include ) && is_array( $include ) ) {
			foreach ( $include as $relation ) {
				if ( method_exists( $item, $relation ) && ! isset( $item->included[ $relation ] ) ) {
					$item->included[ $relation ] = $item->{$relation}();
				}
			}
		}

		return $item;
	}

	/**
	 * Returns a single chatbot user 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 {
			$user = $this->repository->get_item( $request->get_param( 'id' ) );

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

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

	/**
	 * Returns the JSON schema for a chatbot user item.
	 *
	 * Defines structure and validation rules for the chatbot user 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'      => 'chatbot_user',
			'type'       => 'object',
			'properties' => array(
				'id'          => array(
					'description' => __( 'Unique identifier for the chatbot user.', 'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'uuid'        => array(
					'description' => __( 'Unique UUID for the chatbot user.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'wp_user_id'  => array(
					'description' => __( 'WordPress user ID associated with this chatbot user (if applicable).',
						'limb-chatbot' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'device_uuid' => array(
					'description' => __( 'Unique device UUID for tracking.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'ip'          => array(
					'description' => __( 'IP address of the user.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'type'        => array(
					'description' => __( 'User type identifier.', 'limb-chatbot' ),
					'type'        => 'integer',
					'enum'        => array(
						Chatbot_User::TYPE_GUEST,
						Chatbot_User::TYPE_WP_USER,
						Chatbot_User::TYPE_AGENT
					),
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'type_label'  => array(
					'description' => __( 'Human-readable label for the user type.', 'limb-chatbot' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'status'      => array(
					'description' => __( 'User lifecycle status.', 'limb-chatbot' ),
					'type'        => 'integer',
					'enum'        => array(
						Chatbot_User::STATUS_NEW,
						Chatbot_User::STATUS_INTERACTED,
						Chatbot_User::STATUS_BLOCKED,
						Chatbot_User::STATUS_CONVERTED
					),
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'created_at'  => array(
					'description' => __( 'When the chatbot user was created.', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'updated_at'  => array(
					'description' => __( 'When the chatbot user was last updated.', 'limb-chatbot' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'include'     => array(
					'description' => __( 'Include extra data with chatbot user (e.g., name, email, agent, geolocation).',
						'limb-chatbot' ),
					'type'        => 'array',
					'context'     => array( 'view' ),
				),
			),
		);

		return $this->schema;
	}
}
