<?php
declare( strict_types=1 );

namespace FiboFilters\Sorting;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Sorting
 */
class Sorting {
	/** @var CustomSortOptionsProvider */
	private CustomSortOptionsProvider $custom_sort_options_provider;

	public function __construct( CustomSortOptionsProvider $custom_sort_options_provider ) {
		$this->custom_sort_options_provider = $custom_sort_options_provider;
	}

	/**
	 * Get the catalog orderby options
	 *
	 * We don't use 'woocommerce_catalog_orderby' filter, because we don't support custom sorting options.
	 * Each new sorting option should be added to the list of options in this method, needed data should be fetched
	 * in SourceQuery class and sorting should be implemented in JS on the frontend.
	 *
	 * @return array
	 * @see woocommerce_catalog_ordering()
	 */
	public function get_catalog_orderedby(): array {
		// phpcs:disable WordPress.WP.I18n.TextDomainMismatch
		// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
		$show_default_orderby    = apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby', 'menu_order' ) ) === 'menu_order';
		$catalog_orderby_options = [
			'menu_order'    => _x( 'Default sorting', '[front]', 'fibofilters' ),
			'popularity'    => _x( 'Sort by popularity', '[front]', 'fibofilters' ),
			'rating'        => _x( 'Sort by average rating', '[front]', 'fibofilters' ),
			'ratings_count' => _x( 'Sort by number of ratings', '[front]', 'fibofilters' ),
			'date'          => _x( 'Sort by latest', '[front]', 'fibofilters' ),
			'price'         => _x( 'Sort by price: low to high', '[front]', 'fibofilters' ),
			'price-desc'    => _x( 'Sort by price: high to low', '[front]', 'fibofilters' ),
			'modified-desc' => _x( 'Sort by last modified', '[front]', 'fibofilters' ),
			'title'         => _x( 'Sort by name, A-Z', '[front]', 'fibofilters' ),
			'title-desc'    => _x( 'Sort by name, Z-A', '[front]', 'fibofilters' ),
		];

		if ( wc_get_loop_prop( 'is_search' ) ) {
			$catalog_orderby_options = array_merge( [ 'relevance' => _x( 'Relevance', '[front]', 'fibofilters' ) ], $catalog_orderby_options );

			unset( $catalog_orderby_options['menu_order'] );
		}

		if ( ! $show_default_orderby ) {
			unset( $catalog_orderby_options['menu_order'] );
		}

		if ( ! wc_review_ratings_enabled() ) {
			unset( $catalog_orderby_options['rating'] );
		}
		// phpcs:enable WordPress.WP.I18n.TextDomainMismatch

		$allowed_options_base = [
			'menu_order',
			'popularity',
			'rating',
			'date',
			'price',
			'price-desc',
			'relevance',
		];

		foreach ( $this->get_custom_sort_options() as $item ) {
			$catalog_orderby_options[ $item['value'] ] = $item['label'];
			$allowed_options_base[]                    = $item['value'];
		}

		// List of allowed orderby options.
		$allowed_options = apply_filters(
			'fibofilters/allowed_catalog_orderby',
			$allowed_options_base
		);

		foreach ( $catalog_orderby_options as $key => $value ) {
			if ( ! in_array( $key, $allowed_options, true ) ) {
				unset( $catalog_orderby_options[ $key ] );
			}
		}

		return $catalog_orderby_options;
	}

	public function get_custom_sort_options(): array {
		return $this->custom_sort_options_provider->get();
	}

	public function get_custom_orderby_source_data( string $orderby_key ): array {
		$source             = '';
		$order              = '';
		$orderby_product_id = false;

		foreach ( $this->get_custom_sort_options() as $option ) {
			if ( $option['value'] === $orderby_key ) {
				if ( $option['source'] === 'postmeta' ) {
					$source = 'cf__' . $option['postmeta_key'];
					$order  = $option['order'];
				}
				break;
			}
		}

		return [ $source, $order, $orderby_product_id ];
	}

	/**
	 * Get SQL parts for WP_Query for custom orderby
	 *
	 * @param string $orderby_value Orderby value, e.g. 'number-desc'.
	 */
	public function get_custom_orderby_query( string $orderby_value ): array {
		global $wpdb;

		$join_sql    = '';
		$orderby_sql = '';

		$matched_options = wp_filter_object_list( $this->get_custom_sort_options(), [ 'value' => $orderby_value ] );

		if ( ! empty( $matched_options ) ) {
			$option = reset( $matched_options );
			// Sort by numeric postmeta.
			if ( $option['source'] === 'postmeta' && $option['type'] === 'numeric' ) {
				$join_sql    = $wpdb->prepare( " LEFT JOIN $wpdb->postmeta pm_custom_sort ON ($wpdb->posts.ID = pm_custom_sort.post_id AND pm_custom_sort.meta_key = %s)", $option['postmeta_key'] );
				$orderby_sql = 'CAST(pm_custom_sort.meta_value AS UNSIGNED)';
				if ( in_array( $option['order'], [ 'asc', 'desc' ], true ) ) {
					$orderby_sql .= ' ' . strtoupper( $option['order'] );
				}
			}
		}

		return [ $join_sql, $orderby_sql ];
	}
}
