<?php
declare( strict_types=1 );

namespace FiboFilters\Filters\Types;

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

/**
 * Class Category
 */
class Category extends Filter {
	/**
	 * This is the name of this filter type.
	 *
	 * @var string
	 */
	protected $filter_type = 'category';

	/**
	 * @var array
	 */
	protected $contextual_values = [];

	protected function get_data_internal(): array {
		$data = parent::get_data_internal();

		$data = $this->add_contextual_args_to_value_urls( $data );

		$data['current']        = false;
		$data['parent']         = false;
		$data['is_subcategory'] = false;

		if ( $this->context['conditions']['is_product_taxonomy'] && $this->context['details']['taxonomy'] === 'product_cat' ) {
			$term = get_term( $this->context['details']['term_id'], 'product_cat' );

			if ( ! is_object( $term ) || ! is_a( $term, '\WP_Term' ) ) {
				return $data;
			}

			$data['current'] = [
				'name' => $term->name,
			];

			$name = '';
			$url  = '';

			if ( $term->parent > 0 ) {
				$parent_id = $term->parent;

				/**
				 * If there are hidden terms, we need to skip them and retrieve data from the nearest non-hidden parent.
				 */
				$hidden_term_ids = apply_filters( 'fibofilters/indexer/filters_source_query/hidden_term_ids/source=' . $this->get_source(), [] );

				do {
					$continue_flag = false;
					$term_parent   = get_term( $parent_id, 'product_cat' );
					if ( ! is_object( $term_parent ) || ! is_a( $term_parent, '\WP_Term' ) ) {
						return $data;
					}

					$data['is_subcategory'] = true;

					if ( in_array( $term_parent->term_id, $hidden_term_ids, true ) && $term_parent->parent > 0 ) {
						$continue_flag = true;
						$parent_id     = $term_parent->parent;
					} elseif ( in_array( $term_parent->term_id, $hidden_term_ids, true ) && $term_parent->parent === 0 ) {
						$url                    = wc_get_page_permalink( 'shop' );
						$name                   = get_post_field( 'post_title', wc_get_page_id( 'shop' ) );
						$data['is_subcategory'] = false;
					} else {
						$url  = get_term_link( $term_parent );
						$name = $term_parent->name;
					}
				} while ( $continue_flag );
			} else {
				if ( $this->context['conditions']['is_product_search'] ) {
					// When we have a search, we don't go back to the store page, but to the search results page.
					$url  = home_url( '/' );
					$name = _x( 'Search results', '[front] The name of the search results page', 'fibofilters' );
				} else {
					$url  = wc_get_page_permalink( 'shop' );
					$name = get_post_field( 'post_title', wc_get_page_id( 'shop' ) );
				}
			}

			$data['parent'] = [
				'name' => $name,
				'url'  => $url,
			];
		}

		return $data;
	}

	private function add_contextual_args_to_value_urls( array $data ): array {
		if ( ! empty( $data['values'] ) ) {
			/**
			 * If we pass the term object to the get_term_link() function,
			 * it will not be fetched from the database with a separate query.
			 */
			$term_ids  = wp_list_pluck( $data['values'], 'value' );
			$terms     = get_terms(
				[
					'include'    => $term_ids,
					'taxonomy'   => 'product_cat',
					'hide_empty' => false,
				]
			);
			$terms_arr = [];
			if ( is_array( $terms ) ) {
				foreach ( $terms as $term ) {
					$terms_arr[ $term->term_id ] = $term;
				}
			}

			// Remove non-exists terms.
			$data['values'] = array_values(
				array_filter(
					$data['values'],
					function ( $value ) use ( $terms_arr ) {
						return isset( $terms_arr[ (int) $value['value'] ] );
					}
				)
			);

			foreach ( $data['values'] as $index => $value ) {
				$term = $terms_arr[ (int) $value['value'] ] ?? (int) $value['value'];
				$url  = get_term_link( $term, 'product_cat' );
				if ( ! empty( $this->contextual_values ) ) {
					$url = add_query_arg( $this->contextual_values, $url );
				}
				$data['values'][ $index ]['url'] = $url;
			}
		}

		return $data;
	}

	/**
	 * Filter values based on context
	 *
	 * @param array $values Filter values.
	 *
	 * @return array
	 */
	protected function filter_values( array $values ): array {
		$this->set_prop( 'showing_sibling_categories', false );

		$term_parent = $this->context['details']['term_parent'] ?? 0;

		/**
		 * If hidden terms are present, the parent might be among them,
		 * so a different, non-hidden parent must be assigned. We can do it based on the list of values.
		 */
		$hidden_term_ids = apply_filters( 'fibofilters/indexer/filters_source_query/hidden_term_ids/source=' . $this->get_source(), [] );
		if ( ! empty( $hidden_term_ids ) && in_array( $term_parent, $hidden_term_ids, true ) ) {
			foreach ( $values as $value ) {
				if ( $value['value'] === (string) $this->context['details']['term_id'] ) {
					$term_parent = (int) $value['parent_value'];
					break;
				}
			}
		}

		if (
			$this->context['conditions']['is_product_taxonomy'] &&
			$this->context['details']['taxonomy'] === 'product_cat' &&
			$term_parent === 0
		) {
			// Children of zero-level category or zero-level categories.
			$result = array_filter(
				$values,
				function ( array $value ): bool {
					return $value['parent_value'] === (string) $this->context['details']['term_id'];
				}
			);
			if ( ! empty( $result ) ) {
				$values = $result;
			} else {
				$values = array_filter(
					$values,
					function ( array $value ): bool {
						return $value['parent_value'] === '0';
					}
				);
				$this->set_prop( 'showing_sibling_categories', true );
			}
		} elseif (
			$this->context['conditions']['is_product_taxonomy'] &&
			$this->context['details']['taxonomy'] === 'product_cat' &&
			$term_parent > 0
		) {
			// Category children or sibling categories to the current one.
			$result = array_filter(
				$values,
				function ( array $value ): bool {
					return $value['parent_value'] === (string) $this->context['details']['term_id'];
				}
			);
			if ( ! empty( $result ) ) {
				$values = $result;
			} else {
				$values = array_filter(
					$values,
					function ( array $value ) use ( $term_parent ): bool {
						return $value['parent_value'] === (string) $term_parent;
					}
				);
				$this->set_prop( 'showing_sibling_categories', true );
			}
		} else {
			// Only zero-level categories.
			return array_filter(
				$values,
				function ( array $value ): bool {
					return $value['parent_value'] === '0';
				}
			);
		}

		return $values;
	}
}
