<?php
declare( strict_types=1 );

namespace FiboFilters\Filtering;

use FiboFilters\Filters\FilterSources;
use FiboFilters\Filters\StoreFilters;
use FiboFilters\Util\FilterUtil;
use WC_Cache_Helper;
use WP_Query;

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

/**
 * Class VariationAdjuster
 */
class VariationAdjuster {
	/** @var bool */
	private $skip_post_type_link_filter = false;

	/** @var FilterSources */
	private $filter_sources;

	/** @var StoreFilters */
	private $store_filters;

	public function __construct(
		StoreFilters $store_filters,
		FilterSources $filter_sources
	) {
		$this->store_filters  = $store_filters;
		$this->filter_sources = $filter_sources;
	}

	public function adjust_variation_images_and_urls_by_get_vars( array $get_vars, array $filters_affecting_variation_images ) {
		if ( empty( $filters_affecting_variation_images ) || empty( $get_vars ) ) {
			return;
		}

		$query_items = [];

		// Sanitize GET variables and left only those that affect variation images and URLs.
		foreach ( $get_vars as $get_var_key => $get_var_value ) {
			$key = FilterUtil::sanitize_value( $get_var_key );
			if ( in_array( $key, $filters_affecting_variation_images, true ) ) {
				$values = array_map(
					[
						'FiboFilters\Util\FilterUtil',
						'sanitize_value',
					],
					explode( ',', $get_var_value )
				);
				if ( ! empty( $key ) && ! empty( $values ) ) {
					$query_items[ $key ] = $values;
				}
			}
		}

		$this->adjust_variation_images_and_urls( $query_items );
	}

	public function adjust_singe_products_variation_images_and_urls_by_query_items( array $query_items_raw, array $filters_affecting_variation_images ) {
		if ( empty( $query_items_raw ) || empty( $filters_affecting_variation_images ) ) {
			return;
		}

		$query_items = [];

		// Sanitize query items and left only those that affect variation images and URLs.
		foreach ( $query_items_raw as $item ) {
			$key = FilterUtil::sanitize_value( $item['key'] );
			if ( in_array( $key, $filters_affecting_variation_images, true ) ) {
				$values = array_map(
					[
						'FiboFilters\Util\FilterUtil',
						'sanitize_value',
					],
					explode( ',', $item['valueRaw'] )
				);
				if ( ! empty( $key ) && ! empty( $values ) ) {
					$query_items[ $key ] = $values;
				}
			}
		}

		$this->adjust_variation_images_and_urls( $query_items );
	}

	private function adjust_variation_images_and_urls( $query_items ) {
		if ( empty( $query_items ) ) {
			return;
		}

		add_filter(
			'woocommerce_product_get_image_id',
			function ( $image_id ) use ( $query_items ) {
				if (
					is_main_query() ||
					did_action( 'fibofilters/request/single_products/before_loop' )
				) {
					$new_image_id = $this->get_best_match_variation_image_id( get_the_ID(), $query_items );
					if ( $new_image_id > 0 ) {
						return $new_image_id;
					}
				}

				return $image_id;
			}
		);

		add_filter(
			'post_type_link',
			function ( $permalink, $post ) use ( $query_items ) {
				if ( $post->post_type !== 'product' ) {
					return $permalink;
				}
				// Prevent infinite loop with 'post_type_link' filter.
				if ( $this->skip_post_type_link_filter ) {
					return $permalink;
				}

				if (
					is_main_query() ||
					did_action( 'fibofilters/request/single_products/before_loop' )
				) {
					$this->skip_post_type_link_filter = true;
					$new_permalink                    = $this->get_best_match_variation_permalink( $post->ID, $query_items );
					$this->skip_post_type_link_filter = false;
					if ( ! empty( $new_permalink ) ) {
						return $new_permalink;
					}
				}

				return $permalink;
			},
			10,
			2
		);
	}

	private function get_best_match_variation_image_id( int $product_id, array $query_items = [] ): int {
		$cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . '_image_id_' . md5( wp_json_encode( $query_items ) );
		$image_id  = wp_cache_get( $cache_key, 'products' );
		if ( $image_id !== false ) {
			return intval( $image_id );
		}

		$image_id = 0;

		$variation_id = $this->get_best_match_variation_id( $product_id, $query_items );

		if ( $variation_id > 0 ) {
			$thumbnail_id = (int) get_post_meta( $variation_id, '_thumbnail_id', true );
			if ( $thumbnail_id > 0 ) {
				$image_id = $thumbnail_id;
			}
		}

		wp_cache_set( $cache_key, $image_id, 'products' );

		return $image_id;
	}

	private function get_best_match_variation_permalink( int $product_id, array $query_items ) {
		$cache_key = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . '_variation_permalink_' . md5( wp_json_encode( $query_items ) );
		$permalink = wp_cache_get( $cache_key, 'products' );
		if ( $permalink !== false ) {
			return $permalink;
		}

		$permalink = '';

		$variation_id = $this->get_best_match_variation_id( $product_id, $query_items );

		if ( $variation_id > 0 ) {
			$new_permalink = get_permalink( $variation_id );
			if ( ! empty( $new_permalink ) ) {
				$permalink = $new_permalink;
			}
		}

		wp_cache_set( $cache_key, $permalink, 'products' );

		return $permalink;
	}

	private function get_best_match_variation_id( int $product_id, array $query_items ): int {
		$cache_key    = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . '_variation_id_' . md5( wp_json_encode( $query_items ) );
		$variation_id = wp_cache_get( $cache_key, 'products' );
		if ( $variation_id !== false ) {
			return intval( $variation_id );
		}

		$variation_id = 0;

		if ( empty( $query_items ) ) {
			return $variation_id;
		}

		$product = wc_get_product( $product_id );
		if ( $product->get_type() !== 'variable' ) {
			return $variation_id;
		}

		$user_attributes_converted = [];

		foreach ( $query_items as $attribute => $values ) {
			$source = $this->store_filters->get_filter_prop_by_other_prop_value( 'source', 'url_slug', $attribute );
			if ( empty( $source ) ) {
				continue;
			}
			$source_obj = $this->filter_sources->get_source( $source );
			if ( $source_obj->get_data_attr( 'sourceType' ) === 'taxonomy' ) {
				$raw_value                               = $source_obj->get_data_attr( 'rawValue' );
				$user_attributes_converted[ $raw_value ] = $values;
			}
		}

		$match_attributes = [];

		foreach ( $product->get_attributes() as $attribute ) {
			if ( ! $attribute->get_variation() ) {
				continue;
			}
			$attribute_name = $attribute->get_name();
			if ( array_key_exists( $attribute_name, $user_attributes_converted ) ) {
				$match_attributes[ wc_variation_attribute_name( $attribute_name ) ] = $user_attributes_converted[ $attribute_name ];
			}
		}

		$args = [
			'post_parent'    => $product_id,
			'post_type'      => 'product_variation',
			'meta_query'     => [
				'relation' => 'AND',
			],
			'orderby'        => 'ID',
			'order'          => 'ASC',
			'posts_per_page' => 1,
			'fields'         => 'ids',
		];

		foreach ( $match_attributes as $key => $values ) {
			$sub = [
				'relation' => 'OR',
			];
			foreach ( $values as $value ) {
				$sub[] = [
					'key'     => $key,
					'value'   => $value,
					'compare' => '=',
				];
			}
			$args['meta_query'][] = $sub;
		}

		$query = new WP_Query( $args );

		if ( $query->have_posts() ) {
			$variation_id = intval( $query->posts[0] );
		}

		wp_cache_set( $cache_key, $variation_id, 'products' );

		return $variation_id;
	}
}
