<?php
/**
 * Storefront merchandising: applies a smart category's configured product
 * ordering and out-of-stock behaviour on its category archive page.
 *
 * WooCommerce has no native per-category sort. We hook `woocommerce_product_query`
 * (fires AFTER WooCommerce sets its own catalog ordering, so ours wins and the
 * queried object is reliable) to add the OOS meta filter and stash our config,
 * then rebuild ORDER BY in a very-high-priority `posts_clauses` filter using the
 * fast `wc_product_meta_lookup` table — giving us the final say over ordering
 * regardless of theme (tested on Flatsome).
 *
 * @package SmartCategories
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class SCWC_Frontend_Sort
 */
class SCWC_Frontend_Sort {

	/**
	 * Wire hooks.
	 */
	public static function init() {
		add_action( 'woocommerce_product_query', array( __CLASS__, 'on_product_query' ), 20, 2 );
		add_filter( 'posts_clauses', array( __CLASS__, 'clauses' ), 9999, 2 );
	}

	/**
	 * The smart-category term ID of the current archive, or 0.
	 *
	 * @return int
	 */
	private static function current_term_id() {
		if ( is_admin() ) {
			return 0;
		}
		if ( ! function_exists( 'is_product_category' ) || ! is_product_category() ) {
			return 0;
		}
		return (int) get_queried_object_id();
	}

	/**
	 * On the product query for a smart category archive: apply OOS-hide and
	 * stash the sort config for the clauses filter.
	 *
	 * @param WP_Query $q        Query.
	 * @param object   $wc_query WC_Query.
	 * @return void
	 */
	public static function on_product_query( $q, $wc_query ) {
		if ( ! $q->is_main_query() ) {
			return;
		}
		$term_id = self::current_term_id();
		if ( ! $term_id ) {
			return;
		}
		$cfg = SCWC_Term_Rules::get_sort( $term_id );
		if ( 'default' === $cfg['orderby'] && 'none' === $cfg['oos'] ) {
			return;
		}

		if ( 'hide' === $cfg['oos'] ) {
			$meta_query   = (array) $q->get( 'meta_query' );
			$meta_query[] = array(
				'key'     => '_stock_status',
				'value'   => 'outofstock',
				'compare' => '!=',
			);
			$q->set( 'meta_query', $meta_query );
		}

		// Stash for posts_clauses (which has the final say on ORDER BY).
		$q->set( 'scwc_sort_cfg', $cfg );
	}

	/**
	 * Rebuild ORDER BY (and OOS push-down) using the product meta lookup table.
	 *
	 * @param array    $clauses Query clauses.
	 * @param WP_Query $query   Query.
	 * @return array
	 */
	public static function clauses( $clauses, $query ) {
		$cfg = $query->get( 'scwc_sort_cfg' );
		if ( empty( $cfg ) || ! is_array( $cfg ) ) {
			return $clauses;
		}

		global $wpdb;
		$lookup = $wpdb->prefix . 'wc_product_meta_lookup';

		if ( false === strpos( $clauses['join'], 'scwc_lk' ) ) {
			$clauses['join'] .= " LEFT JOIN {$lookup} scwc_lk ON {$wpdb->posts}.ID = scwc_lk.product_id ";
		}

		$order = array();

		if ( 'push_down' === $cfg['oos'] ) {
			$order[] = "(scwc_lk.stock_status = 'outofstock') ASC";
		}

		switch ( $cfg['orderby'] ) {
			case 'best_selling':
				$order[] = 'scwc_lk.total_sales DESC';
				break;
			case 'price_asc':
				$order[] = 'scwc_lk.min_price ASC';
				break;
			case 'price_desc':
				$order[] = 'scwc_lk.max_price DESC';
				break;
			case 'rating':
				$order[] = 'scwc_lk.average_rating DESC';
				break;
			case 'newest':
				$order[] = "{$wpdb->posts}.post_date DESC";
				break;
			case 'oldest':
				$order[] = "{$wpdb->posts}.post_date ASC";
				break;
			case 'title':
				$order[] = "{$wpdb->posts}.post_title ASC";
				break;
		}

		if ( ! empty( $order ) ) {
			$existing           = trim( (string) $clauses['orderby'] );
			$clauses['orderby'] = implode( ', ', $order ) . ( '' !== $existing ? ', ' . $existing : '' );
		}

		return $clauses;
	}
}
