<?php
/**
 * Multilingual compatibility for the customer PWA.
 *
 * @package FoodOrd
 */

defined( 'ABSPATH' ) || exit;

/**
 * Keeps PWA metadata and URLs aligned with WPML and Polylang.
 */
class Foodord_Pwa_Language {
	private const QUERY_ARG      = 'foodord_pwa_lang';
	private const STRING_CONTEXT = 'FoodOrd PWA';

	/**
	 * Register compatibility hooks.
	 */
	public static function init(): void {
		add_action( 'admin_init', array( self::class, 'register_translatable_strings' ) );
	}

	/**
	 * Register merchant-provided PWA copy with installed translation plugins.
	 */
	public static function register_translatable_strings(): void {
		$strings = array(
			'App name'        => sanitize_text_field( (string) get_option( 'foodord_pwa_app_name', '' ) ),
			'Short name'      => sanitize_text_field( (string) get_option( 'foodord_pwa_short_name', '' ) ),
			'App description' => sanitize_text_field( (string) get_option( 'foodord_pwa_description', '' ) ),
		);

		foreach ( $strings as $name => $value ) {
			if ( '' === $value ) {
				continue;
			}

			if ( self::is_wpml_active() ) {
				self::run_wpml_action( 'wpml_register_single_string', array( self::STRING_CONTEXT, $name, $value ) );
			}

			if ( function_exists( 'pll_register_string' ) ) {
				pll_register_string( 'foodord_pwa_' . sanitize_key( $name ), $value, self::STRING_CONTEXT, false );
			}
		}
	}

	/**
	 * Return the private query parameter used by dynamic PWA endpoints.
	 */
	public static function get_query_arg(): string {
		return self::QUERY_ARG;
	}

	/**
	 * Resolve the active or explicitly requested language code.
	 */
	public static function get_current_language(): string {
		$requested = filter_input( INPUT_GET, self::QUERY_ARG, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$requested = is_string( $requested ) ? sanitize_key( $requested ) : '';
		if ( '' !== $requested && self::is_active_language( $requested ) ) {
			return $requested;
		}

		if ( self::is_wpml_active() ) {
			$language = self::apply_wpml_filter( 'wpml_current_language', array( null ) );
			if ( is_string( $language ) && '' !== $language ) {
				return sanitize_key( $language );
			}
		}

		if ( function_exists( 'pll_current_language' ) ) {
			$language = pll_current_language( 'slug' );
			if ( is_string( $language ) && '' !== $language ) {
				return sanitize_key( $language );
			}
		}

		return self::locale_to_language( determine_locale() );
	}

	/**
	 * Return active languages with locale and language home URL.
	 *
	 * @return array<int, array{code:string,locale:string,home_url:string}>
	 */
	public static function get_active_languages(): array {
		$languages = self::get_wpml_languages();
		if ( empty( $languages ) ) {
			$languages = self::get_polylang_languages();
		}

		if ( empty( $languages ) ) {
			$code        = self::get_current_language();
			$languages[] = array(
				'code'     => $code,
				'locale'   => determine_locale(),
				'home_url' => home_url( '/' ),
			);
		}

		return $languages;
	}

	/**
	 * Prepare WordPress and WPML gettext for a dynamic endpoint language.
	 *
	 * @param string $language Language code.
	 */
	public static function activate_language( string $language ): void {
		$language = sanitize_key( $language );
		if ( '' === $language ) {
			return;
		}

		if ( self::is_wpml_active() ) {
			self::run_wpml_action( 'wpml_switch_language', array( $language ) );
		}

		$locale = self::get_language_locale( $language );
		if ( '' !== $locale && function_exists( 'switch_to_locale' ) ) {
			switch_to_locale( $locale );
		}
	}

	/**
	 * Translate a merchant-provided PWA option.
	 *
	 * @param string $value    Original option value.
	 * @param string $name     Stable string name.
	 * @param string $language Target language code.
	 */
	public static function translate_string( string $value, string $name, string $language = '' ): string {
		if ( '' === $value ) {
			return '';
		}

		$language = '' !== $language ? sanitize_key( $language ) : self::get_current_language();
		if ( self::is_wpml_active() ) {
			$translated = self::apply_wpml_filter(
				'wpml_translate_single_string',
				array( $value, self::STRING_CONTEXT, $name, $language )
			);
			if ( is_string( $translated ) && '' !== $translated ) {
				return $translated;
			}
		}

		if ( function_exists( 'pll_translate_string' ) ) {
			$translated = pll_translate_string( $value, $language );
			if ( is_string( $translated ) && '' !== $translated ) {
				return $translated;
			}
		}

		return $value;
	}

	/**
	 * Return a translated post ID, falling back to the original post.
	 *
	 * @param int    $post_id  Original post ID.
	 * @param string $language Target language code.
	 */
	public static function translate_post_id( int $post_id, string $language ): int {
		if ( $post_id <= 0 || '' === $language ) {
			return $post_id;
		}

		if ( self::is_wpml_active() ) {
			$translated = self::apply_wpml_filter( 'wpml_object_id', array( $post_id, get_post_type( $post_id ), true, $language ) );
			$translated = absint( $translated );

			return $translated > 0 ? $translated : $post_id;
		}

		if ( function_exists( 'pll_get_post' ) ) {
			$translated = pll_get_post( $post_id, $language );
			$translated = absint( $translated );

			return $translated > 0 ? $translated : $post_id;
		}

		return $post_id;
	}

	/**
	 * Convert a URL to the requested language.
	 *
	 * @param string $url      Source URL.
	 * @param string $language Target language code.
	 */
	public static function localize_url( string $url, string $language ): string {
		if ( '' === $url || '' === $language ) {
			return $url;
		}

		if ( self::is_wpml_active() ) {
			$translated = self::apply_wpml_filter( 'wpml_permalink', array( $url, $language, true ) );
			return is_string( $translated ) && '' !== $translated ? $translated : $url;
		}

		if ( function_exists( 'pll_get_post' ) ) {
			$post_id = url_to_postid( $url );
			if ( $post_id > 0 ) {
				$translated_id = absint( pll_get_post( $post_id, $language ) );
				if ( $translated_id > 0 ) {
					$translated = get_permalink( $translated_id );
					if ( is_string( $translated ) && '' !== $translated ) {
						return $translated;
					}
				}
			}
		}

		return $url;
	}

	/**
	 * Return the home URL for a specific language.
	 *
	 * @param string $language Language code.
	 */
	public static function get_language_home_url( string $language ): string {
		if ( function_exists( 'pll_home_url' ) ) {
			$url = pll_home_url( $language );
			if ( is_string( $url ) && '' !== $url ) {
				return trailingslashit( $url );
			}
		}

		$home = trailingslashit( (string) get_option( 'home', home_url( '/' ) ) );
		if ( self::is_wpml_active() ) {
			$url = self::apply_wpml_filter( 'wpml_permalink', array( $home, $language, false ) );
			if ( is_string( $url ) && '' !== $url ) {
				return trailingslashit( $url );
			}
		}

		return trailingslashit( home_url( '/' ) );
	}

	/**
	 * Return a BCP 47 language tag for the manifest.
	 *
	 * @param string $language Language code.
	 */
	public static function get_language_tag( string $language ): string {
		$locale = self::get_language_locale( $language );
		$locale = '' !== $locale ? $locale : $language;

		return str_replace( '_', '-', $locale );
	}

	/**
	 * Return the locale attached to a language code.
	 *
	 * @param string $language Language code.
	 */
	private static function get_language_locale( string $language ): string {
		foreach ( self::get_active_languages() as $entry ) {
			if ( $language === $entry['code'] ) {
				return $entry['locale'];
			}
		}

		return '';
	}

	/**
	 * Determine whether a requested language is active.
	 *
	 * @param string $language Language code.
	 */
	private static function is_active_language( string $language ): bool {
		foreach ( self::get_active_languages_without_fallback() as $entry ) {
			if ( $language === $entry['code'] ) {
				return true;
			}
		}

		return self::locale_to_language( determine_locale() ) === $language;
	}

	/**
	 * Return plugin-provided languages without recursively adding a fallback.
	 *
	 * @return array<int, array{code:string,locale:string,home_url:string}>
	 */
	private static function get_active_languages_without_fallback(): array {
		$languages = self::get_wpml_languages();

		return ! empty( $languages ) ? $languages : self::get_polylang_languages();
	}

	/**
	 * Return active WPML languages.
	 *
	 * @return array<int, array{code:string,locale:string,home_url:string}>
	 */
	private static function get_wpml_languages(): array {
		if ( ! self::is_wpml_active() ) {
			return array();
		}

		$active = self::apply_wpml_filter( 'wpml_active_languages', array( null, array( 'skip_missing' => 0 ) ) );
		if ( ! is_array( $active ) ) {
			return array();
		}

		$languages = array();
		foreach ( $active as $entry ) {
			if ( ! is_array( $entry ) || empty( $entry['language_code'] ) ) {
				continue;
			}

			$code        = sanitize_key( (string) $entry['language_code'] );
			$languages[] = array(
				'code'     => $code,
				'locale'   => isset( $entry['default_locale'] ) ? sanitize_text_field( (string) $entry['default_locale'] ) : $code,
				'home_url' => self::get_language_home_url( $code ),
			);
		}

		return $languages;
	}

	/**
	 * Return active Polylang languages.
	 *
	 * @return array<int, array{code:string,locale:string,home_url:string}>
	 */
	private static function get_polylang_languages(): array {
		if ( ! function_exists( 'pll_languages_list' ) || ! function_exists( 'pll_home_url' ) ) {
			return array();
		}

		$codes   = pll_languages_list( array( 'fields' => 'slug' ) );
		$locales = pll_languages_list( array( 'fields' => 'locale' ) );
		if ( ! is_array( $codes ) ) {
			return array();
		}

		$languages = array();
		foreach ( array_values( $codes ) as $index => $code ) {
			$code = sanitize_key( (string) $code );
			if ( '' === $code ) {
				continue;
			}

			$locale      = is_array( $locales ) && isset( $locales[ $index ] ) ? (string) $locales[ $index ] : $code;
			$languages[] = array(
				'code'     => $code,
				'locale'   => sanitize_text_field( $locale ),
				'home_url' => trailingslashit( (string) pll_home_url( $code ) ),
			);
		}

		return $languages;
	}

	/**
	 * Detect WPML without requiring its optional String Translation module.
	 */
	private static function is_wpml_active(): bool {
		return defined( 'ICL_SITEPRESS_VERSION' ) || class_exists( 'SitePress' );
	}

	/**
	 * Apply an optional WPML filter without binding FoodOrd to WPML classes.
	 *
	 * @param string $hook Hook name.
	 * @param array  $args Filter arguments, beginning with the filtered value.
	 * @return mixed
	 */
	private static function apply_wpml_filter( string $hook, array $args ) {
		return call_user_func_array( 'apply_filters', array_merge( array( $hook ), $args ) );
	}

	/**
	 * Run an optional WPML action without binding FoodOrd to WPML classes.
	 *
	 * @param string $hook Hook name.
	 * @param array  $args Action arguments.
	 */
	private static function run_wpml_action( string $hook, array $args ): void {
		call_user_func_array( 'do_action', array_merge( array( $hook ), $args ) );
	}

	/**
	 * Reduce a WordPress locale to a stable language code.
	 *
	 * @param string $locale WordPress locale.
	 */
	private static function locale_to_language( string $locale ): string {
		$parts = preg_split( '/[_-]/', $locale );

		return sanitize_key( is_array( $parts ) && isset( $parts[0] ) ? $parts[0] : $locale );
	}
}
