<?php
declare( strict_types=1 );

namespace FiboFilters\Util;

/**
 * Class FilterUtils
 */
class FilterUtil {
	/**
	 * Filter values and preserve hierarchy
	 *
	 * This method allow to transform terms tree from A -> B -> C -> D to A -> C -> D, A -> D or B -> D, etc.
	 *
	 * @param array $values Values to filter.
	 * @param array $excluded_values Excluded values.
	 */
	public static function filter_values_preserve_hierarchy( array $values, array $excluded_values ): array {
		// Convert all values to string to avoid type mismatch.
		$excluded_values = array_map( 'strval', $excluded_values );

		// Dictionary:  [value_key] => value_array.
		$values_index = [];
		foreach ( $values as $value ) {
			$values_index[ $value['value'] ] = $value;
		}

		// Search for nearest not excluded parent or set '0' as parent.
		$resolve_parent = function ( string $parent_id ) use ( $values_index, $excluded_values ): string {
			while ( $parent_id !== '0' && in_array( $parent_id, $excluded_values, true ) ) {
				$parent_id = $values_index[ $parent_id ]['parent_value'] ?? '0';
			}

			return $parent_id;
		};

		// Process all values.
		$adjusted = array_map(
			function ( array $cat ) use ( $resolve_parent ) {
				$cat['parent_value'] = $resolve_parent( $cat['parent_value'] );

				return $cat;
			},
			$values
		);

		// Remove excluded values, re-index array and return it.
		return array_values(
			array_filter(
				$adjusted,
				function ( array $cat ) use ( $excluded_values ) {
					return ! in_array( $cat['value'], $excluded_values, true );
				}
			)
		);
	}

	/**
	 * Get all children of a given parent value
	 *
	 * This method recursively finds all children of a given parent value in the provided values array.
	 * It resets the parent value to '0' for top-level children.
	 *
	 * @param array   $values Array of values where each value is an associative array with keys 'value' and 'parent_value'.
	 * @param integer $parent_value The parent value for which to find children.
	 * @param bool    $reset_top_level If true, resets the parent value of top-level children to '0'.
	 */
	public static function get_children( array $values, $parent_value, bool $reset_top_level = true ): array {
		$all_children = [];

		foreach ( $values as $value ) {
			if ( (string) $value['parent_value'] === (string) $parent_value ) {
				if ( $reset_top_level ) {
					$value['parent_value'] = '0';
				}

				$all_children[] = $value;

				$children     = self::get_children( $values, $value['value'], false );
				$all_children = array_merge( $children, $all_children );
			}
		}

		return $all_children;
	}

	public static function get_parents_recursive( $all_values, $values ): array {
		$parents = [];

		foreach ( $all_values as $value ) {
			if ( in_array( $value['value'], $values, true ) ) {
				$parents[] = $value;
				$parents   = array_merge( $parents, self::get_parents_recursive( $all_values, [ $value['parent_value'] ] ) );
			}
		}

		return $parents;
	}

	/**
	 * Check if value is "true"
	 *
	 * @param string $value Value.
	 */
	public static function is_filter_true_value( string $value ): bool {
		$value = strtolower( $value );

		return in_array( $value, [ 'yes', '1', 'true', 'on', 'instock' ], true );
	}

	/**
	 * Check if value is "false"
	 *
	 * @param string $value Value.
	 */
	public static function is_filter_false_value( string $value ): bool {
		$value = strtolower( $value );

		return in_array( $value, [ 'no', '0', 'false', 'off' ], true );
	}

	/**
	 * Prepare a text value in order to be displayed in the URL in proper way.
	 *
	 * @param string $label Param name or param value.
	 *
	 * @return string
	 * @see wc_sanitize_taxonomy_name() - based on this function.
	 * @see https://github.com/woocommerce/woocommerce/issues/21026
	 */
	public static function format_url_label( string $label ): string {

		$slug = self::remove_greek_accents( $label );
		$slug = urldecode( sanitize_title( urldecode( $slug ) ) );

		/*
		 * If label is '-' or include only special chars, sanitize_title function could return empty string.
		 * In these cases we have to regenerate url_label in the different way.
		 */
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode, See: https://github.com/WordPress/WordPress-Coding-Standards/issues/1234#issuecomment-561435233
		$url_label = ! empty( $slug ) ? $slug : 'ff' . str_replace( '%', '', strtolower( urlencode( $label ) ) );

		return apply_filters( 'fibofilters/filter_value/url_label', $url_label, $label );
	}

	/**
	 * Sanitize values
	 *
	 * @param string $value Value to sanitize.
	 */
	public static function sanitize_value( string $value ): string {
		// Break early when the value is empty.
		if ( $value === '' ) {
			return '';
		}

		$value = self::remove_greek_accents( $value );

		// Replace dash with placeholder at beginning and end of the string.
		if ( $value[0] === '-' ) {
			$value = 'dash_placeholder' . mb_substr( $value, 1 );
		}
		if ( $value[ mb_strlen( $value ) - 1 ] === '-' ) {
			$value = mb_substr( $value, 0, - 1 ) . 'dash_placeholder';
		}
		// Replace dot with placeholder in the middle of the string.
		$value = str_replace( '.', 'dot_placeholder', $value );

		/**
		 * The $value can be long, especially when multiple values are selected within a single filter.
		 * To prevent the length from being truncated by the sanitize_title() function,
		 * we split the string by commas, sanitize each part individually, and then rejoin them.
		 */
		$value_arr = explode( ',', $value );
		$value_arr = array_map( 'sanitize_title', $value_arr );

		$value = implode( ',', $value_arr );

		$value = urldecode( $value );

		// Revert placeholders.
		return str_replace( [ 'dash_placeholder', 'dot_placeholder' ], [ '-', '.' ], $value );
	}

	/**
	 * Remove Greek accents.
	 *
	 * @param string $text The text to process.
	 *
	 * @return string
	 */
	public static function remove_greek_accents( string $text ): string {
		$chars = [
			'Ά' => 'Α',
			'ά' => 'α',
			'Έ' => 'Ε',
			'έ' => 'α',
			'Ί' => 'Ι',
			'ί' => 'ι',
			'ΐ' => 'ϊ',
			'Ύ' => 'Υ',
			'ύ' => 'υ',
			'ΰ' => 'ϋ',
			'Ή' => 'Η',
			'ή' => 'η',
			'Ό' => 'Ο',
			'ό' => 'ο',
			'Ώ' => 'Ω',
			'ώ' => 'ω',
		];

		return strtr( $text, $chars );
	}
}
