<?php
namespace DisableWPSearch;

use WP_PowerSuite\Core\AbstractModule;

class Module extends AbstractModule {

	private const MODULE_ID = 'disable-wp-search';
	private const MODULE_VERSION = '1.4.1';

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Disable WordPress Search', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Disables default front-end and REST search for blog-style content. Post-type–scoped searches (for example WooCommerce products) stay available. Blocks /wp/v2/search and ?search= on REST post collections.', 'wp-powersuite' );
	}

	public function getContext(): array {
		return [ 'frontend', 'rest' ];
	}

	protected function registerHooks(): void {
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			add_filter( 'rest_endpoints', [ $this, 'removeSearchRestEndpoint' ], 999 );
			add_filter( 'rest_pre_dispatch', [ $this, 'blockRestSearchRequests' ], 999, 3 );
			return;
		}

		if ( is_admin() || ( function_exists( 'wp_doing_cron' ) && wp_doing_cron() ) ) {
			return;
		}

		add_action( 'parse_query', [ $this, 'disableSearchQuery' ], 999 );
		add_action( 'template_redirect', [ $this, 'blockSearchPage' ], 999 );
	}

	/**
	 * @param array<string, mixed> $endpoints Registered REST routes.
	 * @return array<string, mixed>
	 */
	public function removeSearchRestEndpoint( array $endpoints ): array {
		unset( $endpoints['/wp/v2/search'] );

		return $endpoints;
	}

	/**
	 * Block REST collection searches for blog-style post types.
	 *
	 * @param mixed               $result  Response to replace the requested version with.
	 * @param \WP_REST_Server     $server  Server instance.
	 * @param \WP_REST_Request    $request Request used to generate the response.
	 * @return mixed
	 */
	public function blockRestSearchRequests( $result, \WP_REST_Server $server, \WP_REST_Request $request ) {
		unset( $server );

		$search = $request->get_param( 'search' );
		if ( $search === null || $search === '' ) {
			return $result;
		}

		if ( $this->restSearchIsAllowed( $request ) ) {
			return $result;
		}

		return new \WP_Error(
			'rest_search_disabled',
			__( 'Search is disabled.', 'wp-powersuite' ),
			[ 'status' => 403 ]
		);
	}

	public function disableSearchQuery( \WP_Query $query ): void {
		if ( is_admin() || ! $query->is_main_query() ) {
			return;
		}

		if ( $this->isSpecializedPostTypeSearch( $query ) ) {
			return;
		}

		if ( $this->requestHasBlockedSearchQuery() || $query->is_search() ) {
			$query->set_404();
			$query->is_search = false;
			$query->is_404    = true;
			$query->query_vars['s'] = false;
			$query->query['s']      = false;
		}
	}

	public function blockSearchPage(): void {
		if ( ! $this->requestHasBlockedSearchQuery() && ! is_search() ) {
			return;
		}

		if ( $this->isSpecializedPostTypeSearchRequest() ) {
			return;
		}

		global $wp_query;
		$wp_query->posts         = [];
		$wp_query->post_count    = 0;
		$wp_query->found_posts   = 0;
		$wp_query->max_num_pages = 0;
		$wp_query->set_404();
		$wp_query->is_search  = false;
		$wp_query->is_404     = true;
		$wp_query->is_home    = false;
		$wp_query->is_archive = false;
		status_header( 404 );
		nocache_headers();
	}

	private function requestHasBlockedSearchQuery(): bool {
		if ( ! isset( $_GET['s'] ) ) {
			return false;
		}

		$term = sanitize_text_field( wp_unslash( (string) $_GET['s'] ) );

		return $term !== '';
	}

	private function isSpecializedPostTypeSearch( \WP_Query $query ): bool {
		$post_type = $query->get( 'post_type' );

		return $this->postTypeQueryIsSpecialized( $post_type );
	}

	private function isSpecializedPostTypeSearchRequest(): bool {
		if ( isset( $_GET['post_type'] ) ) {
			$raw = wp_unslash( $_GET['post_type'] );
			if ( is_array( $raw ) ) {
				$types = array_values( array_unique( array_map( 'sanitize_key', array_filter( array_map( 'strval', $raw ) ) ) ) );

				return $this->postTypeListIsSpecialized( $types );
			}
			$one = sanitize_key( (string) $raw );

			return $this->singlePostTypeIsSpecialized( $one );
		}

		global $wp_query;
		if ( $wp_query instanceof \WP_Query ) {
			return $this->isSpecializedPostTypeSearch( $wp_query );
		}

		return false;
	}

	/**
	 * @param mixed $post_type Query var from WP_Query::get( 'post_type' ).
	 */
	private function postTypeQueryIsSpecialized( mixed $post_type ): bool {
		if ( $post_type === '' || $post_type === false || $post_type === null ) {
			return false;
		}
		if ( ! is_array( $post_type ) ) {
			return $this->singlePostTypeIsSpecialized( sanitize_key( (string) $post_type ) );
		}
		$types = array_values( array_unique( array_map( 'sanitize_key', array_filter( array_map( 'strval', $post_type ) ) ) ) );

		return $this->postTypeListIsSpecialized( $types );
	}

	private function postTypeListIsSpecialized( array $types ): bool {
		if ( $types === [] ) {
			return false;
		}

		$allow = $this->getSearchAllowlistPostTypes();
		foreach ( $types as $t ) {
			if ( in_array( $t, $allow, true ) ) {
				return true;
			}
		}

		if ( in_array( 'post', $types, true ) ) {
			return false;
		}

		return true;
	}

	private function singlePostTypeIsSpecialized( string $post_type ): bool {
		if ( $post_type === '' || $post_type === 'any' ) {
			return false;
		}

		if ( in_array( $post_type, $this->getSearchAllowlistPostTypes(), true ) ) {
			return true;
		}

		return $post_type !== 'post';
	}

	private function restSearchIsAllowed( \WP_REST_Request $request ): bool {
		$post_type = $this->getRestPostTypeFromRequest( $request );
		if ( $post_type === null || $post_type === '' ) {
			return false;
		}

		return $this->singlePostTypeIsSpecialized( $post_type );
	}

	private function getRestPostTypeFromRequest( \WP_REST_Request $request ): ?string {
		$route = $request->get_route();
		if ( ! is_string( $route ) || ! preg_match( '#^/wp/v2/([^/]+)#', $route, $matches ) ) {
			return null;
		}

		$rest_base = $matches[1];
		if ( $rest_base === 'search' ) {
			return null;
		}

		foreach ( get_post_types( [ 'show_in_rest' => true ], 'objects' ) as $post_type_object ) {
			if ( ! $post_type_object instanceof \WP_Post_Type ) {
				continue;
			}

			$object_rest_base = $post_type_object->rest_base ?: $post_type_object->name;
			if ( $object_rest_base === $rest_base ) {
				return $post_type_object->name;
			}
		}

		return null;
	}

	/**
	 * Always allow front-end search for these post types (e.g. keep ?post_type=product&s=).
	 *
	 * `apply_filters( 'wp_powersuite_disable_search_allow_post_types', [] )` → string[]
	 *
	 * @return array<int, string>
	 */
	private function getSearchAllowlistPostTypes(): array {
		$types = apply_filters( 'wp_powersuite_disable_search_allow_post_types', [] );
		if ( ! is_array( $types ) ) {
			return [];
		}

		return array_values( array_filter( array_map( 'sanitize_key', $types ) ) );
	}
}
