<?php

namespace AIPT\Utils\ProductFilter;

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

/**
 * PolylangTermHandler - Polylang multilingual compatibility for term queries.
 *
 * Centralizes all Polylang-specific logic for category/tag term handling:
 * - Bypasses Polylang's language filtering in REST API context
 * - Deduplicates translated terms so each logical term appears once
 * - Expands term IDs to include all translations for inclusive filtering
 *
 * Every public method is a transparent no-op when Polylang is not active,
 * so callers never need their own function_exists() guards.
 *
 * Works with both free Polylang (different slugs per translation)
 * and Polylang Pro (shared slugs across translations).
 *
 * @since 2.4.5
 */
class PolylangTermHandler {

	/**
	 * Whether Polylang plugin is active.
	 *
	 * @return bool
	 */
	public static function isActive(): bool {
		return function_exists( 'pll_current_language' );
	}

	/**
	 * Adjusts get_terms() arguments for Polylang compatibility.
	 *
	 * Adds 'lang' => '' to bypass Polylang's language filtering and retrieve
	 * terms from ALL languages. Necessary because pll_current_language()
	 * returns empty in REST API context, causing unpredictable filtering.
	 *
	 * No-op when Polylang is not active.
	 *
	 * @param array $args get_terms() arguments (modified by reference).
	 * @return void
	 */
	public static function adjustTermQueryArgs( array &$args ): void {
		if ( self::isActive() ) {
			$args['lang'] = '';
		}
	}

	/**
	 * Deduplicates Polylang term translations so each logical term appears once.
	 *
	 * Primary: pll_get_term_translations() for accurate translation grouping
	 * (works with both free Polylang and Pro regardless of slug strategy).
	 * Fallback: slug-based grouping when pll_get_term_translations is unavailable
	 * (only effective when translations share the same slug, e.g. Pro).
	 *
	 * In each group the default-language version is preferred for display.
	 *
	 * No-op when Polylang is not active.
	 *
	 * @param array $terms Array of WP_Term objects.
	 * @return array Deduplicated array of WP_Term objects.
	 */
	public static function deduplicateTerms( array $terms ): array {
		if ( ! function_exists( 'pll_default_language' ) ) {
			return $terms;
		}

		$default_lang = pll_default_language();

		if ( ! is_string( $default_lang ) || '' === $default_lang ) {
			return $terms;
		}

		if ( function_exists( 'pll_get_term_translations' ) ) {
			return self::deduplicateByTranslationGroup( $terms, $default_lang );
		}

		if ( function_exists( 'pll_get_term_language' ) ) {
			return self::deduplicateBySlug( $terms, $default_lang );
		}

		return $terms;
	}

	/**
	 * Expands resolved term IDs to include all their Polylang translations.
	 *
	 * When a slug resolves to term ID 123 (e.g. English "Essential Oils"),
	 * this expands it to [123, 456, 789, …] (all language translations).
	 * Ensures filtering finds products in ALL language versions of the category.
	 *
	 * Critical for free Polylang where translations have different slugs,
	 * so slug-based resolution only finds one language's term.
	 *
	 * No-op when Polylang is not active or pll_get_term_translations unavailable.
	 *
	 * @param int[] $term_ids Array of resolved term IDs.
	 * @return int[] Expanded array including all translation term IDs.
	 */
	public static function expandTermIdsWithTranslations( array $term_ids ): array {
		if ( ! function_exists( 'pll_get_term_translations' ) || empty( $term_ids ) ) {
			return $term_ids;
		}

		$expanded = [];
		$seen     = [];

		foreach ( $term_ids as $tid ) {
			$tid = (int) $tid;
			if ( isset( $seen[ $tid ] ) ) {
				continue;
			}

			$translations = pll_get_term_translations( $tid );

			if ( is_array( $translations ) && ! empty( $translations ) ) {
				foreach ( $translations as $translated_tid ) {
					$translated_tid = (int) $translated_tid;
					if ( ! isset( $seen[ $translated_tid ] ) ) {
						$expanded[]                = $translated_tid;
						$seen[ $translated_tid ]   = true;
					}
				}
			} else {
				if ( ! isset( $seen[ $tid ] ) ) {
					$expanded[]    = $tid;
					$seen[ $tid ]  = true;
				}
			}
		}

		return $expanded;
	}

	/**
	 * Deduplicates terms using Polylang's translation relationship API.
	 *
	 * @param array  $terms        Array of WP_Term objects.
	 * @param string $default_lang Default language code (e.g. 'en').
	 * @return array Deduplicated array of WP_Term objects.
	 */
	private static function deduplicateByTranslationGroup( array $terms, string $default_lang ): array {

		$terms_by_id = [];
		foreach ( $terms as $term ) {
			if ( is_object( $term ) && isset( $term->term_id ) ) {
				$terms_by_id[ $term->term_id ] = $term;
			}
		}

		$processed_ids = [];
		$result        = [];

		foreach ( $terms as $term ) {
			if ( ! is_object( $term ) || ! isset( $term->term_id ) ) {
				continue;
			}

			if ( isset( $processed_ids[ $term->term_id ] ) ) {
				continue;
			}

			$translations = pll_get_term_translations( $term->term_id );

			if ( is_array( $translations ) && ! empty( $translations ) ) {
				foreach ( $translations as $tid ) {
					$processed_ids[ (int) $tid ] = true;
				}
			} else {

				$processed_ids[ $term->term_id ] = true;
			}

			$preferred = $term;
			if ( $default_lang && is_array( $translations ) && isset( $translations[ $default_lang ] ) ) {
				$default_tid = (int) $translations[ $default_lang ];
				if ( isset( $terms_by_id[ $default_tid ] ) ) {
					$preferred = $terms_by_id[ $default_tid ];
				}
			}

			$result[] = $preferred;
		}

		return $result;
	}

	/**
	 * Deduplicates terms by grouping on shared slugs (fallback).
	 *
	 * Only effective when translations share the same slug
	 * (e.g. Polylang Pro with slug sync enabled).
	 *
	 * @param array  $terms        Array of WP_Term objects.
	 * @param string $default_lang Default language code (e.g. 'en').
	 * @return array Deduplicated array of WP_Term objects.
	 */
	private static function deduplicateBySlug( array $terms, string $default_lang ): array {
		$groups = [];
		foreach ( $terms as $term ) {
			if ( ! is_object( $term ) || ! isset( $term->slug ) ) {
				continue;
			}
			$groups[ $term->slug ][] = $term;
		}

		$result = [];
		foreach ( $groups as $group_terms ) {
			if ( count( $group_terms ) === 1 ) {
				$result[] = $group_terms[0];
				continue;
			}

			$preferred = $group_terms[0];
			if ( $default_lang ) {
				foreach ( $group_terms as $t ) {
					if ( pll_get_term_language( $t->term_id ) === $default_lang ) {
						$preferred = $t;
						break;
					}
				}
			}
			$result[] = $preferred;
		}

		return $result;
	}
}
