<?php
/**
 * The ajax search class.
 *
 * @package woodmart
 */

namespace XTS\Modules\Search;

use XTS\Singleton;
use WP_Query;

/**
 * The ajax search class.
 */
class Ajax_Search extends Singleton {
	/**
	 * Init.
	 */
	public function init() {
		add_action( 'wp_ajax_woodmart_ajax_search', array( $this, 'get_ajax_suggestions' ), 10 );
		add_action( 'wp_ajax_nopriv_woodmart_ajax_search', array( $this, 'get_ajax_suggestions' ), 10 );
	}

	/**
	 * Get the ajax search suggestions.
	 *
	 * @return void
	 */
	public function get_ajax_suggestions() {
		$main_suggestions = $this->get_main_suggestions();
		$cat_suggestions  = $this->get_product_categories_suggestions();
		$blog_suggestions = $this->get_blog_suggestions();

		$suggestions = $this->build_suggestions( $main_suggestions, $cat_suggestions, $blog_suggestions );

		wp_send_json(
			array(
				'suggestions' => $suggestions,
			)
		);
	}

	/**
	 * Get the divider for the suggestions.
	 *
	 * @param string $group The group name.
	 *
	 * @return array The divider data.
	 */
	public function get_divider( $group ) {
		switch ( $group ) {
			case 'portfolio':
				$divider = esc_html__( 'Projects', 'woodmart' );
				break;
			case 'page':
				$divider = esc_html__( 'Pages', 'woodmart' );
				break;
			case 'post':
				$divider = esc_html__( 'Blog posts', 'woodmart' );
				break;
			default:
				$divider = esc_html__( 'Products', 'woodmart' );
				break;
		}

		return array(
			'value'   => '',
			'divider' => $divider,
			'group'   => $group,
		);
	}

	/**
	 * Get the main suggestions.
	 *
	 * @return array The main suggestions.
	 */
	public function get_main_suggestions() {
		$post_type  = $this->get_post_type();
		$query_args = $this->build_query_args( $post_type );

		if ( woodmart_get_opt( 'show_out_of_stock_at_the_end' ) && 'product' === $post_type && woodmart_woocommerce_installed() ) {
			add_filter( 'posts_clauses', array( $this, 'sort_outofstock_products_last_ajax' ), 10 );
		}

		$query = new WP_Query( $query_args );

		if ( woodmart_get_opt( 'show_out_of_stock_at_the_end' ) && 'product' === $post_type && woodmart_woocommerce_installed() ) {
			remove_filter( 'posts_clauses', array( $this, 'sort_outofstock_products_last_ajax' ), 10 );
		}

		$this->apply_relevanssi_filter( $query );

		if ( $query->have_posts() ) {
			$suggestions = $this->get_query_suggestions( $query, $post_type );
		} else {
			$suggestions = array(
				array(
					'value'              => ( 'product' === $post_type ) ? esc_html__( 'No products found', 'woodmart' ) : esc_html__( 'No items found', 'woodmart' ),
					'products_not_found' => true,
					'permalink'          => '',
					'group'              => $post_type,
				),
			);
		}

		return $suggestions;
	}

	/**
	 * Get the product categories suggestions.
	 *
	 * @return array The product categories suggestions.
	 */
	public function get_product_categories_suggestions() {
		if ( ! woodmart_woocommerce_installed() ) {
			return array();
		}

		$post_type = $this->get_post_type();
		$search    = sanitize_text_field( $_REQUEST['query'] );

		if ( ! isset( $_REQUEST['include_cat_search'] ) || 'yes' !== $_REQUEST['include_cat_search'] || 'product' !== $post_type || empty( $search ) ) {
			return array();
		}

		$suggestions  = array();
		$search_query = array(
			'taxonomy'   => 'product_cat',
			'number'     => ! empty( $_REQUEST['number'] ) ? (int) $_REQUEST['number'] : 5,
			'orderby'    => 'name',
			'search'     => $search,
			'hide_empty' => false,
		);

		if ( ! empty( $_REQUEST['product_cat'] ) ) {
			$category               = get_term_by( 'slug', sanitize_text_field( $_REQUEST['product_cat'] ), 'product_cat' );
			$search_query['parent'] = $category->term_id;
		}

		$categories = get_categories( $search_query );

		if ( empty( $categories ) ) {
			return array();
		}

		$suggestions[] = array(
			'value'   => '',
			'divider' => esc_html__( 'Categories', 'woodmart' ),
			'group'   => 'categories',
		);

		foreach ( $categories as $category ) {
			$category_name = $category->name;
			$icon_data     = get_term_meta( $category->term_taxonomy_id, 'category_icon_alt', true );
			$icon_html     = $this->get_category_icon( $category_name, $icon_data );

			$data = array(
				'value'     => $category_name,
				'permalink' => get_term_link( $category ),
				'group'     => 'categories',
			);

			if ( $icon_html ) {
				$data['thumbnail'] = $icon_html;
			}

			$suggestions[] = $data;
		}

		return $suggestions;
	}

	/**
	 * Get the category icon.
	 *
	 * @param string $category_name The category name.
	 * @param array  $icon_data     The icon data.
	 *
	 * @return string The category icon HTML.
	 */
	public function get_category_icon( $category_name, $icon_data ) {
		$image_output = '';

		if ( empty( $icon_data['id'] ) ) {
			return '';
		}

		$image_output .= wp_get_attachment_image(
			$icon_data['id'],
			'woocommerce_thumbnail',
			false,
			array(
				'alt'     => esc_attr( $category_name ),
				'loading' => ! wp_lazy_loading_enabled( 'img', '' ) ? 'lazy' : '',
			)
		);

		return $image_output;
	}

	/**
	 * Get the blog suggestions.
	 *
	 * @return array The blog suggestions.
	 */
	public function get_blog_suggestions() {
		$post_type = $this->get_post_type();

		if ( ! woodmart_get_opt( 'enqueue_posts_results' ) || in_array( $post_type, array( 'post', 'any' ), true ) ) {
			return array();
		}

		$query_args  = $this->build_query_args( 'post' );
		$query       = new WP_Query( $query_args );
		$suggestions = $this->get_query_suggestions( $query, 'post' );

		return $suggestions;
	}

	/**
	 * Build the suggestions array.
	 *
	 * @param array $main_suggestions   The main suggestions.
	 * @param array $cat_suggestions    The category suggestions.
	 * @param array $blog_suggestions   The blog suggestions.
	 *
	 * @return array The combined suggestions.
	 */
	public function build_suggestions( $main_suggestions, $cat_suggestions, $blog_suggestions ) {
		$post_type   = $this->get_post_type();
		$suggestions = $cat_suggestions;

		if ( ! empty( $cat_suggestions ) || ! empty( $blog_suggestions ) ) {
			$suggestions[] = $this->get_divider( $post_type );
		}

		$suggestions = array_merge( $suggestions, $main_suggestions );

		if ( ! empty( $blog_suggestions ) ) {
			$suggestions[] = $this->get_divider( 'post' );
			$suggestions   = array_merge( $suggestions, $blog_suggestions );
		}

		if ( 1 === count( $suggestions ) && isset( $suggestions[0]['products_not_found'] ) ) {
			$suggestions[0]['no_results'] = true;
		}

		return $suggestions;
	}

	/**
	 * Get the query suggestions.
	 *
	 * @param WP_Query $query     The WP_Query object.
	 * @param string   $post_type The post type.
	 *
	 * @return array The query suggestions.
	 */
	public function get_query_suggestions( $query, $post_type ) {
		$suggestions = array();

		while ( $query->have_posts() ) {
			$query->the_post();

			if ( 'product' === $post_type && woodmart_woocommerce_installed() ) {
				$product = wc_get_product( get_the_ID() );

				$suggestions[] = array(
					'value'     => html_entity_decode( get_the_title() ),
					'permalink' => get_the_permalink(),
					'price'     => $product->get_price_html(),
					'thumbnail' => $product->get_image(),
					'sku'       => $product->get_sku() ? esc_html__( 'SKU:', 'woodmart' ) . ' ' . $product->get_sku() : '',
					'group'     => $post_type,
				);
			} else {
				$suggestions[] = array(
					'value'     => html_entity_decode( get_the_title() ),
					'permalink' => get_the_permalink(),
					'thumbnail' => get_the_post_thumbnail( null, 'medium', '' ),
					'group'     => $post_type,
				);
			}
		}

		wp_reset_postdata();

		return $suggestions;
	}

	/**
	 * Build the query arguments.
	 *
	 * @param string $post_type The post type.
	 *
	 * @return array The query arguments.
	 */
	public function build_query_args( $post_type ) {
		$query_args = array(
			'posts_per_page' => ! empty( $_REQUEST['number'] ) ? (int) $_REQUEST['number'] : 5,
			'post_status'    => 'publish',
			'post_type'      => $post_type,
			'no_found_rows'  => 1,
		);

		if ( 'product' === $post_type && woodmart_woocommerce_installed() ) {
			$query_args['tax_query']  = $this->build_product_tax_query();
			$query_args['meta_query'] = $this->build_product_meta_query();
		}

		if ( ! empty( $_REQUEST['query'] ) ) {
			$query_args['s'] = sanitize_text_field( $_REQUEST['query'] );
		}

		return apply_filters( 'woodmart_ajax_search_args', $query_args, $post_type );
	}

	/**
	 * Build the product tax query.
	 *
	 * @return array The product tax query.
	 */
	public function build_product_tax_query() {
		$product_visibility_term_ids = wc_get_product_visibility_term_ids();

		$tax_query = array(
			'relation' => 'AND',
			array(
				'taxonomy' => 'product_visibility',
				'field'    => 'term_taxonomy_id',
				'terms'    => $product_visibility_term_ids['exclude-from-search'],
				'operator' => 'NOT IN',
			),
			array(
				'taxonomy' => 'product_visibility',
				'field'    => 'name',
				'terms'    => array( 'exclude-from-search' ),
				'operator' => 'NOT IN',
			),
		);

		if ( ! empty( $_REQUEST['product_cat'] ) ) {
			$tax_query[] = array(
				'taxonomy' => 'product_cat',
				'field'    => 'slug',
				'terms'    => strip_tags( $_REQUEST['product_cat'] ),
			);
		}

		return $tax_query;
	}

	/**
	 * Build the product meta query.
	 *
	 * @return array The product meta query.
	 */
	public function build_product_meta_query() {
		$meta_query = array();

		if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
			$meta_query[] = array(
				'key'     => '_stock_status',
				'value'   => 'outofstock',
				'compare' => 'NOT IN',
			);
		}

		return $meta_query;
	}

	/**
	 * Get the post type from the request.
	 *
	 * @return string The post type.
	 */
	public function get_post_type() {
		$allowed_types = array( 'post', 'product', 'portfolio', 'any', 'page' );
		$post_type     = 'product';

		if ( ! empty( $_REQUEST['post_type'] ) && in_array( $_REQUEST['post_type'], $allowed_types ) ) {
			$post_type = strip_tags( $_REQUEST['post_type'] );
		}

		return $post_type;
	}

	/**
	 * Apply the Relevanssi filter to the query.
	 *
	 * @param WP_Query $query The WP_Query object.
	 */
	public function apply_relevanssi_filter( $query ) {
		if ( woodmart_get_opt( 'relevanssi_search' ) && function_exists( 'relevanssi_do_query' ) ) {
			add_filter( 'relevanssi_hits_filter', array( $this, 'update_hits_filter_by_product_sku' ), 10, 2 );
			relevanssi_do_query( $query );
		}
	}

	/**
	 * Update the hits filter by product SKU.
	 *
	 * @param array    $filter_data The filter data.
	 * @param WP_Query $query      The WP_Query object.
	 *
	 * @return array The updated filter data.
	 */
	public function update_hits_filter_by_product_sku( $filter_data, $query ) {
		if ( ! apply_filters( 'woodmart_search_by_sku', woodmart_get_opt( 'search_by_sku' ) ) || ! isset( $query->query['post_type'] ) || 'product' !== $query->query['post_type'] ) {
			return $filter_data;
		}

		$args = array(
			'post_type'      => 'product',
			'posts_per_page' => -1,
			'meta_query'     => array(
				array(
					'key'     => '_sku',
					'value'   => $query->query['s'],
					'compare' => 'LIKE',
				),
				array(
					'key'     => '_visibility',
					'value'   => array( 'catalog', 'hidden' ),
					'compare' => 'NOT IN',
				),
			),
		);

		$posts = get_posts( $args );

		if ( $posts ) {
			$product_ids = array_column( (array) $filter_data[0], 'ID' );

			foreach ( $posts as $post ) {
				if ( ! in_array( apply_filters( 'wpml_object_id', $post->ID, 'product', true ), $product_ids ) ) {
					array_unshift( $filter_data[0], $post );
				}
			}
		}

		return $filter_data;
	}

	/**
	 * Sort out-of-stock products to display last in AJAX search.
	 *
	 * @param array $clauses Associative array of the clauses for the query.
	 *
	 * @return array Modified clauses.
	 */
	public function sort_outofstock_products_last_ajax( $clauses ) {
		if ( class_exists( '\XTS\Modules\Out_Of_Stock_Manager\Main' ) ) {
			return \XTS\Modules\Out_Of_Stock_Manager\Main::apply_stock_sorting( $clauses, 'stock_status_meta_ajax' );
		}

		return $clauses;
	}
}

Ajax_Search::get_instance();
