<?php

namespace Simple_History;

use Simple_History\Services\AddOns_Licences;
use Simple_History\Services\Failed_Login_Limit_Service;
use WP_Error;
use WP_REST_Controller;
use WP_REST_Server;

/**
 * REST API controller for retrieving search options,
 * i.e. data required for the search options in the admin UI.
 * It does not search anything. A better name would be nice.
 */
class WP_REST_SearchOptions_Controller extends WP_REST_Controller {
	/**
	 * Simple History instance.
	 *
	 * @var Simple_History
	 */
	protected Simple_History $simple_history;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->namespace      = 'simple-history/v1';
		$this->rest_base      = 'search-options';
		$this->simple_history = Simple_History::get_instance();
	}

	/**
	 * Register the routes for the objects of the controller.
	 */
	public function register_routes() {
		// GET /wp-json/simple-history/v1/search-options.
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_items' ],
					'permission_callback' => [ $this, 'get_items_permissions_check' ],
				],
			],
		);

		// GET /wp-json/simple-history/v1/search-user.
		register_rest_route(
			$this->namespace,
			'/search-user',
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [ $this, 'get_items_for_search_user' ],
					'permission_callback' => [ $this, 'get_items_permissions_check_for_search_user' ],
				],
			],
		);
	}

	/**
	 * Get items for search user.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response|\WP_Error Response object or error.
	 */
	public function get_items_for_search_user( $request ) {
		$data = [];

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$q = trim( sanitize_text_field( wp_unslash( $_GET['q'] ?? '' ) ) );

		if ( empty( $q ) ) {
			return new WP_Error(
				'rest_invalid_param',
				__( 'Please provide a search query.', 'simple-history' ),
				[ 'status' => 400 ]
			);
		}

		$data = $this->search_user( $q );

		return rest_ensure_response( $data );
	}

	/**
	 * Checks if a given request has access to read posts.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return true|\WP_Error True if the request has read access, WP_Error object otherwise.
	 */
	public function get_items_permissions_check( $request ) {
		// User must be logged in.
		if ( ! is_user_logged_in() ) {
			return new WP_Error(
				'rest_forbidden_context',
				__( 'Sorry, you are not allowed to view events.', 'simple-history' ),
				[ 'status' => rest_authorization_required_code() ]
			);
		}

		// User must be allowed to view the history log.
		// Unlike the events endpoint, this payload is not filtered per logger:
		// it carries site wide data (event counts, activity date range, add-on
		// list and versions, feature flags and the premium maps API key), so
		// logged in alone is not a sufficient gate.
		// phpcs:ignore WordPress.WP.Capabilities.Undetermined -- Dynamic capability from Helpers::get_view_history_capability().
		if ( ! current_user_can( Helpers::get_view_history_capability() ) ) {
			return new WP_Error(
				'rest_forbidden_context',
				__( 'Sorry, you are not allowed to view events.', 'simple-history' ),
				[ 'status' => rest_authorization_required_code() ]
			);
		}

		return true;
	}

	/**
	 * Checks if a given request has access to read posts.
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 * @return true|\WP_Error True if the request has read access, WP_Error object otherwise.
	 */
	public function get_items_permissions_check_for_search_user( $request ) {
		// User must be logged in.
		if ( ! is_user_logged_in() ) {
			return new WP_Error(
				'rest_forbidden_context',
				__( 'Sorry, you are not allowed to search users.', 'simple-history' ),
				[ 'status' => rest_authorization_required_code() ]
			);
		}

		// user must have list_users capability (default super admin + administrators have this).
		if ( ! current_user_can( 'list_users' ) ) {
			return new WP_Error(
				'rest_forbidden_context',
				__( 'Sorry, you are not allowed to search users.', 'simple-history' ),
				[ 'status' => rest_authorization_required_code() ]
			);
		}

		return true;
	}

	/**
	 * Get items, i.e. get data for search options.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response Response object.
	 */
	public function get_items( $request ) {
		/** @var AddOns_Licences */
		$addons_service = $this->simple_history->get_service( AddOns_Licences::class );

		$has_failed_login_limit = Failed_Login_Limit_Service::is_active();

		$data = [
			'dates'                           => Helpers::get_data_for_date_filter(),
			'loggers'                         => $this->get_loggers_and_messages(),
			'initiators'                      => $this->get_initiator_options(),
			'pager_size'                      => [
				'page'      => (int) Helpers::get_pager_size(),
				'dashboard' => (int) Helpers::get_pager_size_dashboard(),
			],
			'new_events_check_interval'       => Helpers::get_new_events_check_interval(),
			'maps_api_key'                    => apply_filters( 'simple_history/maps_api_key', '' ),
			'addons'                          => [
				'addons'                       => $addons_service->get_addon_plugins(),
				'has_extended_settings_add_on' => $addons_service->has_add_on( 'simple-history-extended-settings' ),
				'has_premium_add_on'           => $addons_service->has_add_on( 'simple-history-premium' ),
			],
			'has_failed_login_limit'          => $has_failed_login_limit,
			'failed_login_limit_threshold'    => $has_failed_login_limit
				? Failed_Login_Limit_Service::get_threshold()
				: 0,
			'failed_login_suppressed_count'   => $has_failed_login_limit
				? Failed_Login_Limit_Service::get_last_suppressed_count()
				: 0,
			'experimental_features_enabled'   => Helpers::experimental_features_is_enabled(),
			'reactions_enabled'               => Helpers::reactions_are_enabled(),
			'events_admin_page_url'           => Helpers::get_history_admin_url(),
			'settings_page_url'               => Helpers::get_settings_page_url(),
			'stats_page_url'                  => admin_url( 'admin.php?page=simple_history_stats_page' ),
			'current_user_id'                 => get_current_user_id(),
			'current_user_can_manage_options' => current_user_can( 'manage_options' ),
			'stats'                           => [
				'num_events_today'       => Helpers::get_num_events_today(),
				'num_events_last_7_days' => Helpers::get_num_events_last_n_days( 7 ),
			],
		];

		/**
		 * Filters the search options data before returning it.
		 *
		 * Allows add-ons to extend the search options response
		 * with additional data (e.g. page URLs, feature flags).
		 *
		 * @since 5.7.0
		 *
		 * @param array $data Search options data.
		 */
		$data = apply_filters( 'simple_history/search_options_data', $data );

		return rest_ensure_response( $data );
	}

	/**
	 * Get loggers and messages.
	 *
	 * @return array
	 */
	protected function get_loggers_and_messages() {
		$simple_history = Simple_History::get_instance();

		$loggers_and_messages  = [];
		$loggers_user_can_read = $simple_history->get_loggers_that_user_can_read();

		foreach ( $loggers_user_can_read as $logger ) {
			$logger_info        = $logger['instance']->get_info();
			$logger_slug        = $logger['instance']->get_slug();
			$logger_name        = $logger_info['name'];
			$logger_search_data = [];

			// Get labels for logger.
			if ( isset( $logger_info['labels']['search'] ) ) {

				// Create array with all search messages for this logger.
				$arr_all_search_messages = [];

				foreach ( $logger_info['labels']['search']['options'] ?? [] as $option_messages ) {
					$arr_all_search_messages = array_merge( $arr_all_search_messages, $option_messages );
				}

				foreach ( $arr_all_search_messages as $key => $val ) {
					$arr_all_search_messages[ $key ] = $logger_slug . ':' . $val;
				}

				// Label for search options, like "Users" or "Post".
				$logger_search_data['search'] = [
					'label'   => $logger_info['labels']['search']['label'],
					'options' => $arr_all_search_messages,
				];

				// Label all = "All found updates" and so on.
				if ( ! empty( $logger_info['labels']['search']['label_all'] ) ) {
					$logger_search_data['search_all'] = [
						'label'   => $logger_info['labels']['search']['label_all'],
						'options' => $arr_all_search_messages,
					];
				}

				// For each specific search option.
				$labels_search_options = $logger_info['labels']['search']['options'] ?? [];
				foreach ( $labels_search_options as $option_key => $option_messages ) {
					foreach ( $option_messages as $key => $val ) {
						$option_messages[ $key ] = $logger_slug . ':' . $val;
					}

					$logger_search_data['search_options'][] = [
						'label'   => $option_key,
						'options' => $option_messages,
					];
				}
			}

			$loggers_and_messages[] = [
				'slug'        => $logger_slug,
				'name'        => $logger_name,
				'search_data' => $logger_search_data,
			];
		}

		return $loggers_and_messages;
	}

	/**
	 * Get initiator options for search filter.
	 *
	 * @return array Array of initiator options with value and label.
	 */
	protected function get_initiator_options() {
		$options = [];

		foreach ( Log_Initiators::get_valid_initiators() as $initiator ) {
			$options[] = [
				'value' => $initiator,
				'label' => Log_Initiators::get_initiator_label( $initiator ),
			];
		}

		return $options;
	}

	/**
	 * Search for users.
	 *
	 * @param string $q Search query.
	 * @return array List of users.
	 */
	public function search_user( $q ) {
		// Search both current users and all logged rows,
		// because a user can change email
		// search in context: user_id, user_email, user_login
		// search in wp_users: login, nicename, user_email
		// search and get users. make sure to use "fields" and "number" or we can get timeout/use lots of memory if we have a large amount of users.
		return get_users(
			[
				'search' => "*{$q}*",
				'fields' => array( 'ID', 'user_login', 'user_nicename', 'user_email', 'display_name' ),
				'number' => 20,
			]
		);
	}
}
