<?php
/**
 * Include/exclude rules helper.
 *
 * @package PRECO
 */

namespace PRECO\Includes\Frontend\Engine\Filters\Query;

defined( 'ABSPATH' ) || exit;

/**
 * Include/exclude rules helper.
 */
class IncludeExcludeRuleHelper {
	/**
	 * Add an explicit product include/exclude rule.
	 *
	 * @param int[]|null $include_ids Accumulated include IDs, modified by reference.
	 * @param int[]      $exclude_ids Accumulated exclude IDs, modified by reference.
	 * @param string     $relation    Rule relation (e.g. 'not_in').
	 * @param mixed      $value       Stored product ID(s).
	 * @return void
	 */
	public function add_product_rule( &$include_ids, &$exclude_ids, $relation, $value ) {
		$ids = is_array( $value ) ? array_map( 'absint', $value ) : array();
		$ids = array_values( array_unique( array_filter( $ids ) ) );
		if ( empty( $ids ) ) {
			// if ( 'not_in' !== $relation ) {
			// $include_ids = array();
			// }.
			return;
		}

		if ( 'not_in' === $relation ) {
			$exclude_ids = array_values( array_unique( array_merge( $exclude_ids, $ids ) ) );
			return;
		}

		if ( null === $include_ids ) {
			$include_ids = $ids;
			return;
		}

		$include_ids = array_values( array_intersect( $include_ids, $ids ) );
	}

	/**
	 * Add an on-sale product include/exclude rule.
	 *
	 * @param int[]|null $include_ids Accumulated include IDs, modified by reference.
	 * @param int[]      $exclude_ids Accumulated exclude IDs, modified by reference.
	 * @param string     $relation    Rule relation (e.g. 'is_not').
	 * @return void
	 */
	public function add_on_sale_rule( &$include_ids, &$exclude_ids, $relation ) {
		if ( ! function_exists( 'wc_get_product_ids_on_sale' ) ) {
			return;
		}
		$on_sale = \wc_get_product_ids_on_sale();
		$on_sale = is_array( $on_sale ) ? array_values( array_unique( array_filter( array_map( 'absint', $on_sale ) ) ) ) : array();
		$on_sale = $this->expand_ids_with_parents( $on_sale );
		if ( empty( $on_sale ) ) {
			if ( 'is' === $relation ) {
				$include_ids = array();
			}
			return;
		}

		if ( 'is_not' === $relation ) {
			$exclude_ids = array_values( array_unique( array_merge( $exclude_ids, $on_sale ) ) );
			return;
		}

		if ( null === $include_ids ) {
			$include_ids = $on_sale;
			return;
		}

		$include_ids = array_values( array_intersect( $include_ids, $on_sale ) );
	}

	/**
	 * Add an include/exclude rule from a WooCommerce-sourced ID list (e.g. related/upsell IDs).
	 *
	 * @param int[]|null $include_ids Accumulated include IDs, modified by reference.
	 * @param int[]      $exclude_ids Accumulated exclude IDs, modified by reference.
	 * @param mixed      $wc_ids      Source product IDs from WooCommerce.
	 * @param string     $relation    Rule relation (e.g. 'is_not').
	 * @return void
	 */
	public function add_wc_source_rule( &$include_ids, &$exclude_ids, $wc_ids, $relation ) {
		$wc_ids = is_array( $wc_ids ) ? array_values( array_unique( array_filter( array_map( 'absint', $wc_ids ) ) ) ) : array();
		if ( empty( $wc_ids ) ) {
			if ( 'is' === $relation ) {
				$include_ids = array();
			}
			return;
		}

		if ( 'is_not' === $relation ) {
			$exclude_ids = array_values( array_unique( array_merge( $exclude_ids, $wc_ids ) ) );
			return;
		}

		if ( null === $include_ids ) {
			$include_ids = $wc_ids;
			return;
		}

		$include_ids = array_values( array_intersect( $include_ids, $wc_ids ) );
	}

	/**
	 * Add a recently-viewed product include/exclude rule.
	 *
	 * @param int[]|null $include_ids Accumulated include IDs, modified by reference.
	 * @param int[]      $exclude_ids Accumulated exclude IDs, modified by reference.
	 * @param string     $relation    Rule relation (e.g. 'not_in').
	 * @return void
	 */
	public function add_recently_viewed_rule( &$include_ids, &$exclude_ids, $relation ) {
		$ids = \PRECO\Includes\Frontend\Core\RecentlyViewedTracker::get_ids();
		if ( empty( $ids ) ) {
			if ( 'in' === $relation ) {
				$include_ids = array();
			}
			return;
		}

		if ( 'not_in' === $relation ) {
			$exclude_ids = array_values( array_unique( array_merge( $exclude_ids, $ids ) ) );
			return;
		}

		if ( null === $include_ids ) {
			$include_ids = $ids;
			return;
		}

		$include_ids = array_values( array_intersect( $include_ids, $ids ) );
	}

	/**
	 * Expand IDs so variation IDs also include their parent product ID.
	 *
	 * @param int[] $ids Product/variation IDs.
	 * @return int[]
	 */
	private function expand_ids_with_parents( $ids ) {
		if ( empty( $ids ) || ! is_array( $ids ) || ! function_exists( 'wp_get_post_parent_id' ) ) {
			return is_array( $ids ) ? $ids : array();
		}

		$expanded = array_values( array_unique( array_filter( array_map( 'absint', $ids ) ) ) );
		foreach ( $expanded as $id ) {
			$parent = absint( \wp_get_post_parent_id( $id ) );
			if ( $parent ) {
				$expanded[] = $parent;
			}
		}

		return array_values( array_unique( array_filter( array_map( 'absint', $expanded ) ) ) );
	}
}
