<?php
namespace TeknoTok\AiTranslator;

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

/**
 * Main Plugin Coordinator class.
 */
class Plugin {
	/**
	 * Maximum target languages allowed for free stores.
	 *
	 * @var int
	 */
	const MAX_FREE_TARGET_LANGUAGES = 1;

	/**
	 * Maximum target languages allowed for premium stores.
	 *
	 * @var int
	 */
	const MAX_PREMIUM_TARGET_LANGUAGES = 15;

	/**
	 * Singleton instance.
	 *
	 * @var Plugin|null
	 */
	private static $instance = null;

	/**
	 * Settings controller.
	 *
	 * @var Admin\SettingsController|null
	 */
	public $settings = null;

	/**
	 * License client.
	 *
	 * @var Integrations\LicenseManager|null
	 */
	public $license = null;

	/**
	 * Queue Manager.
	 *
	 * @var Queue\QueueManager|null
	 */
	public $queue = null;

	/**
	 * WooCommerce integration.
	 *
	 * @var Integrations\WooCommerceIntegration|null
	 */
	public $woocommerce = null;

	/**
	 * SEO integration.
	 *
	 * @var Integrations\SeoIntegration|null
	 */
	public $seo = null;

	/**
	 * Switcher controller.
	 *
	 * @var Frontend\SwitcherController|null
	 */
	public $switcher = null;

	/**
	 * URL manager.
	 *
	 * @var Frontend\UrlManager|null
	 */
	public $url_manager = null;

	/**
	 * Redirect controller.
	 *
	 * @var Frontend\RedirectController|null
	 */
	public $redirect = null;

	/**
	 * Return the singleton.
	 *
	 * @return Plugin
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor.
	 */
	private function __construct() {
		$this->early_init();
		add_action( 'init', array( $this, 'init' ) );
	}

	/**
	 * Run early hook registrations on plugins_loaded.
	 *
	 * @return void
	 */
	private function early_init() {
		// Dynamic locale filter to switch core and WC language packs
		add_filter( 'locale', array( $this, 'set_locale' ), 10 );

		// Filter option value on retrieval to ensure only supported target languages are loaded
		add_filter( 'option_teknotok_ait_settings', array( $this, 'sanitize_settings_option' ) );

		// Cron hook for background downloading of language packs
		add_action( 'teknotok_ait_download_translations', array( $this, 'download_translations_cron' ) );

		// Add expected quick links on the WordPress Plugins screen.
		add_filter( 'plugin_action_links_' . TEKNOTOK_AIT_BASENAME, array( $this, 'add_plugin_action_links' ) );

		// Initialize Schema upgrades if version changed
		self::maybe_upgrade_schema();
	}

	/**
	 * Add quick links to the plugin row on the WordPress Plugins screen.
	 *
	 * @param array<string,string> $links Existing plugin action links.
	 * @return array<string,string> Modified plugin action links.
	 */
	public function add_plugin_action_links( $links ) {
		if ( ! current_user_can( 'manage_options' ) ) {
			return $links;
		}

		$quick_links = array(
			'settings' => sprintf(
				'<a href="%s">%s</a>',
				esc_url( admin_url( 'admin.php?page=teknotok-ait' ) ),
				esc_html__( 'Settings', 'teknotok-ai-translator' )
			),
		);

		if ( ! Integrations\LicenseManager::is_premium_active() ) {
			$quick_links['license'] = sprintf(
				'<a href="%s">%s</a>',
				esc_url( admin_url( 'admin.php?page=teknotok-ait-license' ) ),
				esc_html__( 'License', 'teknotok-ai-translator' )
			);
		}

		return array_merge( $quick_links, $links );
	}

	/**
	 * Wire services and hooks on WordPress init.
	 *
	 * @return void
	 */
	public function init() {
		// Instantiate modules
		$this->license     = new Integrations\LicenseManager();
		$this->queue       = new Queue\QueueManager();
		$this->woocommerce = new Integrations\WooCommerceIntegration();
		$this->seo         = new Integrations\SeoIntegration();
		$this->switcher    = new Frontend\SwitcherController();
		$this->redirect    = new Frontend\RedirectController();
		$this->url_manager = new Frontend\UrlManager();
		new Frontend\TranslationFilters();

		// Schedule check hooks
		add_action( 'teknotok_ait_daily_license_check', array( $this->license, 'daily_check' ) );

		// Auto-queue hook on post saves
		add_action( 'save_post', array( $this, 'auto_queue_post_translations' ), 10, 3 );

		// Hook controllers
		$this->license->init_hooks();

		// Handle Setup action
		add_action( 'admin_init', array( $this, 'handle_setup_action' ) );

		if ( is_admin() ) {
			new Admin\SettingsController( $this );
			new Admin\LicenseController( $this );
			new Admin\DiagnosticController( $this );
			new Admin\EditorController( $this );
			add_action( 'admin_notices', array( $this, 'admin_notices' ) );
		}

		$this->maybe_queue_shared_strings_after_upgrade();
	}

	/**
	 * Get the active language based on GET variables or cookie states.
	 *
	 * @return string Language code.
	 */
	public static function get_active_language() {
		$settings = get_option( 'teknotok_ait_settings', array() );
		$source = $settings['source_language'] ?? 'en';
		$targets = $settings['target_languages'] ?? array();
		$req_path = '';
		$has_request_uri = false;

		// 1. Try to parse from request URI path (for subfolder mode early detection)
		if ( isset( $_SERVER['REQUEST_URI'] ) ) {
			$has_request_uri = true;
			$request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) );
			$req_path = trim( parse_url( $request_uri, PHP_URL_PATH ), '/' );
			// If WordPress is installed in a subdirectory, strip it
			$home_path = parse_url( get_option( 'home' ), PHP_URL_PATH );
			if ( ! empty( $home_path ) && '/' !== $home_path ) {
				$home_path = trim( $home_path, '/' );
				if ( 0 === strpos( $req_path, $home_path ) ) {
					$req_path = trim( substr( $req_path, strlen( $home_path ) ), '/' );
				}
			}
			$segments = explode( '/', $req_path );
			$first_segment = isset( $segments[0] ) ? sanitize_key( $segments[0] ) : '';
			if ( in_array( $first_segment, $targets, true ) ) {
				return $first_segment;
			}
		}

		// 2. Try to get from query vars (if already parsed)
		$lang = '';
		global $wp_query;
		if ( isset( $wp_query ) && $wp_query instanceof \WP_Query && function_exists( 'get_query_var' ) ) {
			$lang = get_query_var( 'lang' );
		}

		// 3. Try to get from GET parameter
		if ( empty( $lang ) && isset( $_GET['lang'] ) ) {
			$lang = sanitize_key( wp_unslash( $_GET['lang'] ) );
		}

		if ( ! empty( $lang ) && ( in_array( $lang, $targets, true ) || $lang === $source ) ) {
			return $lang;
		}

		// Clean, unprefixed frontend URLs are always the source language.
		// This prevents stale translated-language cookies from changing canonical
		// source pages, while keeping explicit target prefixes as the public SEO URLs.
		if ( $has_request_uri && ! self::is_store_api_request() && ! self::is_wordpress_internal_request( $req_path ) ) {
			return $source;
		}

		// 4. WooCommerce Blocks call Store API endpoints outside the translated path.
		// Use the translated page referrer only for Store API requests, not normal page navigation.
		if ( self::is_store_api_request() && isset( $_SERVER['HTTP_REFERER'] ) ) {
			$referer = esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) );
			$ref_path = trim( parse_url( $referer, PHP_URL_PATH ), '/' );
			$home_path = parse_url( get_option( 'home' ), PHP_URL_PATH );
			if ( ! empty( $home_path ) && '/' !== $home_path ) {
				$home_path = trim( $home_path, '/' );
				if ( 0 === strpos( $ref_path, $home_path ) ) {
					$ref_path = trim( substr( $ref_path, strlen( $home_path ) ), '/' );
				}
			}
			$segments = explode( '/', $ref_path );
			$first_segment = isset( $segments[0] ) ? sanitize_key( $segments[0] ) : '';
			if ( in_array( $first_segment, $targets, true ) ) {
				return $first_segment;
			}
		}

		// 5. Try to get from cookie
		if ( isset( $_COOKIE['teknotok_ait_language'] ) ) {
			$lang = sanitize_key( wp_unslash( $_COOKIE['teknotok_ait_language'] ) );
			if ( in_array( $lang, $targets, true ) || $lang === $source ) {
				return $lang;
			}
		}

		return $source;
	}

	/**
	 * Check if the current request is a WooCommerce Store API request.
	 *
	 * @return bool True for Store API requests.
	 */
	private static function is_store_api_request() {
		$request_uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';

		return false !== strpos( $request_uri, '/wp-json/wc/store/' )
			|| false !== strpos( $request_uri, 'rest_route=/wc/store/' );
	}

	/**
	 * Check if a path is a WordPress internal/admin endpoint.
	 *
	 * @param string $req_path Request path relative to home.
	 * @return bool True for internal WordPress paths.
	 */
	private static function is_wordpress_internal_request( $req_path ) {
		return (bool) preg_match( '#^(wp-admin|wp-json|wp-login\.php|xmlrpc\.php)(/|$)#i', (string) $req_path );
	}


	/**
	 * Save post hook to queue translations automatically.
	 *
	 * @param int      $post_id Post ID.
	 * @param \WP_Post $post    Post object.
	 * @param bool     $update  Whether this is an existing post being updated.
	 * @return void
	 */
	public function auto_queue_post_translations( $post_id, $post, $update ) {
		if ( wp_is_post_revision( $post_id ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
			return;
		}


		if ( ! self::is_ready_for_translations() ) {
			return;
		}

		$settings = get_option( 'teknotok_ait_settings', array() );
		$auto_translate = $settings['auto_translate'] ?? '1';

		if ( '1' !== $auto_translate ) {
			return;
		}

		$targets = $settings['target_languages'] ?? array();
		if ( empty( $targets ) ) {
			return;
		}

		if ( 'publish' !== get_post_status( $post_id ) || ! is_post_type_viewable( get_post_type( $post_id ) ) ) {
			return;
		}

		$source_hash = \TeknoTok\AiTranslator\Database\JobRepository::get_post_source_hash( $post_id );
		$permalink   = get_permalink( $post_id );

		\TeknoTok\AiTranslator\Database\JobRepository::suspend_async_triggers( true );
		try {
			foreach ( $targets as $lang ) {
				\TeknoTok\AiTranslator\Database\JobRepository::add_job( 'post', $post_id, $lang, $source_hash );
				if ( $permalink ) {
					\TeknoTok\AiTranslator\Database\JobRepository::add_frontend_warmup_job( $permalink, $lang, $source_hash );
				}
			}
		} finally {
			\TeknoTok\AiTranslator\Database\JobRepository::suspend_async_triggers( false );
		}

		\TeknoTok\AiTranslator\Queue\QueueManager::trigger_async_queue();
	}

	/**
	 * Plugin activation.
	 *
	 * @return void
	 */
	public static function activate() {
		require_once TEKNOTOK_AIT_DIR . 'includes/Database/Schema.php';
		Database\Schema::create_tables();
		Database\Schema::seed_defaults();

		// Schedule daily license check
		if ( ! wp_next_scheduled( 'teknotok_ait_daily_license_check' ) ) {
			wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'teknotok_ait_daily_license_check' );
		}

		update_option( 'teknotok_ait_activation_notice', 1, false );
		update_option( 'teknotok_ait_db_version', TEKNOTOK_AIT_VERSION, false );

		// Send registration ping to licensing server for free usage tracking
		wp_remote_post(
			TEKNOTOK_AIT_LICENSE_API . 'register-free',
			array(
				'method'      => 'POST',
				'timeout'     => 15,
				'redirection' => 5,
				'httpversion' => '1.0',
				'blocking'    => false,
				'headers'     => array(
					'Content-Type' => 'application/json',
				),
				'body'        => wp_json_encode( array(
					'product_slug' => TEKNOTOK_AIT_PRODUCT_SLUG,
					'site_url'     => get_site_url(),
				) ),
			)
		);

		flush_rewrite_rules();
	}

	/**
	 * Plugin deactivation.
	 *
	 * @return void
	 */
	public static function deactivate() {
		$timestamp = wp_next_scheduled( 'teknotok_ait_daily_license_check' );
		if ( $timestamp ) {
			wp_unschedule_event( $timestamp, 'teknotok_ait_daily_license_check' );
		}

		flush_rewrite_rules();
	}

	/**
	 * Run schema migrations on updates.
	 *
	 * @return void
	 */
	private static function maybe_upgrade_schema() {
		require_once TEKNOTOK_AIT_DIR . 'includes/Database/Schema.php';

		if ( get_option( 'teknotok_ait_db_version' ) === TEKNOTOK_AIT_VERSION && Database\Schema::jobs_support_string_payloads() ) {
			return;
		}

		Database\Schema::create_tables();
		Database\Schema::seed_defaults();
		update_option( 'teknotok_ait_queue_shared_strings_after_upgrade', '1', false );
		update_option( 'teknotok_ait_db_version', TEKNOTOK_AIT_VERSION, false );
	}

	/**
	 * Render administrative alerts for WooCommerce and licensing checks.
	 *
	 * @return void
	 */
	public function admin_notices() {
		if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
			return;
		}



		$this->maybe_mark_setup_complete_from_settings();

		// Setup Wizard Notice
		if ( get_option( 'teknotok_ait_setup_complete' ) !== '1' ) {
			$languages = self::get_supported_languages_by_priority();

			$locale = substr( get_locale(), 0, 2 );
			unset( $languages[ $locale ] );

			?>
			<div class="notice notice-info is-dismissible teknotok-setup-notice" style="border-left-color: #6366f1; padding: 20px; border-radius: 12px; background: #ffffff; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); margin-top: 15px;">
				<div style="display: flex; align-items: center; gap: 20px;">
					<div style="background: #e0e7ff; color: #4f46e5; border-radius: 10px; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; font-size: 24px;">
						<span class="dashicons dashicons-translation" style="font-size: 24px; width: 24px; height: 24px; line-height: 24px;"></span>
					</div>
					<div style="flex: 1;">
						<h2 style="margin: 0 0 5px 0; color: #1e293b; font-size: 16px; font-weight: 700;"><?php esc_html_e( 'TeknoTok AI Translator: Setup Wizard', 'teknotok-ai-translator' ); ?></h2>
						<p style="margin: 0; color: #64748b; font-size: 14px;"><?php esc_html_e( 'Choose your target language below to automatically translate your pages, posts, and WooCommerce products in the background!', 'teknotok-ai-translator' ); ?></p>
					</div>
				</div>
				<form method="post" action="" style="margin-top: 18px; display: flex; align-items: center; gap: 15px; flex-wrap: wrap;">
					<?php wp_nonce_field( 'teknotok_ait_setup_wizard' ); ?>
					<input type="hidden" name="teknotok_ait_setup_action" value="save_setup" />
					
					<label for="target_language" style="font-weight: 600; color: #334155; font-size: 13px;"><?php esc_html_e( 'Translate my site to:', 'teknotok-ai-translator' ); ?></label>
					<select name="target_language" id="target_language" style="padding: 6px 12px; border: 1px solid #cbd5e1; border-radius: 6px; background: #fff; min-width: 180px;">
						<?php foreach ( $languages as $code => $name ) : ?>
							<option value="<?php echo esc_attr( $code ); ?>"><?php echo esc_html( $name ); ?></option>
						<?php endforeach; ?>
					</select>
					
					<button type="submit" class="button button-primary" style="background: linear-gradient(135deg, #6366f1, #4f46e5); color: white; border: none; padding: 6px 18px; font-weight: 600; border-radius: 6px; cursor: pointer; height: auto; line-height: 1.5; box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.2);"><?php esc_html_e( 'Start Translating & View Progress', 'teknotok-ai-translator' ); ?></button>
				</form>
			</div>
			<?php
			return; // Don't show regular notices during setup
		}

		return;
	}

	/**
	 * Dynamically switch the WordPress locale based on the active language.
	 *
	 * @param string $locale Current locale.
	 * @return string Modified locale.
	 */
	public function set_locale( $locale ) {
		// Do not change locale in admin panel, to preserve admin interface language
		if ( is_admin() ) {
			return $locale;
		}

		$active_lang = self::get_active_language();
		$settings = get_option( 'teknotok_ait_settings', array() );
		$source = $settings['source_language'] ?? 'en';

		if ( $active_lang === $source ) {
			return $locale;
		}

		return self::get_locale_for_lang( $active_lang );
	}

	/**
	 * Background task to download translations for new locales.
	 *
	 * @param array $targets List of target language codes.
	 * @return void
	 */
	public function download_translations_cron( $targets ) {
		if ( empty( $targets ) ) {
			return;
		}

		$site_url = get_site_url();
		$is_localhost = ( strpos( $site_url, 'localhost' ) !== false || strpos( $site_url, '127.0.0.1' ) !== false );

		if ( ! $is_localhost ) {
			require_once ABSPATH . 'wp-admin/includes/file.php';
			require_once ABSPATH . 'wp-admin/includes/template.php';
			require_once ABSPATH . 'wp-admin/includes/translation-install.php';

			$downloaded_any = false;
			if ( function_exists( 'wp_download_language_pack' ) ) {
				foreach ( $targets as $lang ) {
					$locale_code = self::get_locale_for_lang( $lang );
					// Download WordPress core translation pack
					$downloaded = wp_download_language_pack( $locale_code );
					if ( $downloaded ) {
						$downloaded_any = true;
						\TeknoTok\AiTranslator\Utils\Logger::log( sprintf( 'Downloaded core translation pack for %s (%s)', $lang, $locale_code ), 'info' );
					}
				}
			}

			// Force check for plugin and theme translation updates
			delete_site_transient( 'translation_updates' );
			delete_site_transient( 'update_plugins' );
			delete_site_transient( 'update_themes' );
			wp_clean_plugins_cache();
			wp_clean_themes_cache();
			wp_update_plugins();
			wp_update_themes();

			// Bulk upgrade translations for WooCommerce, plugins, and themes
			require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
			if ( class_exists( '\Language_Pack_Upgrader' ) && class_exists( '\Automatic_Upgrader_Skin' ) ) {
				$upgrader = new \Language_Pack_Upgrader( new \Automatic_Upgrader_Skin() );
				$upgrader->bulk_upgrade();
				\TeknoTok\AiTranslator\Utils\Logger::log( 'Completed bulk translation upgrades for plugins and themes.', 'info' );
			}
		} else {
			\TeknoTok\AiTranslator\Utils\Logger::log( 'Skipped core translation download on localhost to prevent single-thread execution deadlock.', 'info' );
		}

		$this->queue_existing_content_for_targets( $targets );
	}

	/**
	 * Queue existing published content for target languages.
	 *
	 * @param array $targets Target language codes.
	 * @return void
	 */
	public function queue_existing_content_for_targets( $targets ) {
		if ( empty( $targets ) ) {
			return;
		}

		global $wpdb;
		$jobs_table = $wpdb->prefix . 'teknotok_jobs';

		\TeknoTok\AiTranslator\Database\JobRepository::suspend_async_triggers( true );
		try {
			foreach ( $targets as $lang ) {
				$lang = sanitize_key( $lang );
				if ( empty( $lang ) ) {
					continue;
				}

				// 1. Bulk queue all missing posts, pages, products in a single SQL query
				$posts_inserted = $wpdb->query( $wpdb->prepare(
					"INSERT INTO {$jobs_table} (object_type, object_id, target_lang, status, created_at, updated_at)
					SELECT 'post', ID, %s, 'pending', %s, %s
					FROM {$wpdb->posts}
					WHERE post_type IN ('post', 'page', 'product') AND post_status = 'publish'
					  AND ID NOT IN (
						SELECT object_id FROM {$jobs_table} WHERE object_type = 'post' AND target_lang = %s
					  )",
					$lang,
					current_time( 'mysql', true ),
					current_time( 'mysql', true ),
					$lang
				) );

				if ( $posts_inserted > 0 ) {
					\TeknoTok\AiTranslator\Utils\Logger::log( sprintf( 'Bulk queued %d published posts/products/pages for target language %s.', $posts_inserted, $lang ), 'info' );
				}

				// 2. Bulk queue all missing taxonomies in a single SQL query
				$taxonomies = array_filter(
					array_unique( array( 'category', 'post_tag', 'product_cat', 'product_tag' ) ),
					'taxonomy_exists'
				);

				if ( ! empty( $taxonomies ) ) {
					$placeholders = implode( ',', array_fill( 0, count( $taxonomies ), '%s' ) );
					
					$tax_query = $wpdb->prepare(
						"INSERT INTO {$jobs_table} (object_type, object_id, target_lang, status, created_at, updated_at)
						SELECT 'taxonomy', term_id, %s, 'pending', %s, %s
						FROM {$wpdb->term_taxonomy}
						WHERE taxonomy IN ($placeholders)
						  AND term_id NOT IN (
							SELECT object_id FROM {$jobs_table} WHERE object_type = 'taxonomy' AND target_lang = %s
						  )",
						array_merge(
							array( $lang, current_time( 'mysql', true ), current_time( 'mysql', true ) ),
							$taxonomies,
							array( $lang )
						)
					);

					$terms_inserted = $wpdb->query( $tax_query );
					if ( $terms_inserted > 0 ) {
						\TeknoTok\AiTranslator\Utils\Logger::log( sprintf( 'Bulk queued %d taxonomy terms for target language %s.', $terms_inserted, $lang ), 'info' );
					}
				}
			}

			$this->queue_frontend_warmup_jobs_for_targets( $targets );
		} finally {
			\TeknoTok\AiTranslator\Database\JobRepository::suspend_async_triggers( false );
		}

		\TeknoTok\AiTranslator\Queue\QueueManager::trigger_async_queue();
	}

	/**
	 * Mark setup complete when settings already contain a real target language.
	 *
	 * @return void
	 */
	private function maybe_mark_setup_complete_from_settings() {
		if ( get_option( 'teknotok_ait_setup_complete' ) === '1' ) {
			return;
		}

		if ( self::has_valid_target_language_configured() ) {
			update_option( 'teknotok_ait_setup_complete', '1', false );
		}
	}

	/**
	 * Check whether settings include at least one supported target language.
	 *
	 * @return bool True when setup can be considered complete.
	 */
	public static function has_valid_target_language_configured() {
		$settings  = get_option( 'teknotok_ait_settings', array() );
		$source    = sanitize_key( $settings['source_language'] ?? 'en' );
		$targets   = isset( $settings['target_languages'] ) && is_array( $settings['target_languages'] ) ? $settings['target_languages'] : array();
		$supported = array_keys( self::get_supported_languages() );

		foreach ( $targets as $target ) {
			$target = sanitize_key( $target );
			if ( ! empty( $target ) && $target !== $source && in_array( $target, $supported, true ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Queue shared frontend strings once after plugin upgrades.
	 *
	 * @return void
	 */
	private function maybe_queue_shared_strings_after_upgrade() {
		if ( '1' !== get_option( 'teknotok_ait_queue_shared_strings_after_upgrade', '0' ) ) {
			return;
		}

		$settings = get_option( 'teknotok_ait_settings', array() );
		$targets  = $settings['target_languages'] ?? array();

		$this->queue_frontend_shared_string_jobs_for_targets( $targets );
		delete_option( 'teknotok_ait_queue_shared_strings_after_upgrade' );
	}

	/**
	 * Queue shared frontend strings for target languages.
	 *
	 * @param array $targets Target language codes.
	 * @return void
	 */
	private function queue_frontend_shared_string_jobs_for_targets( $targets ) {
		if ( empty( $targets ) ) {
			return;
		}

		$shared_strings = $this->get_frontend_shared_strings();
		if ( empty( $shared_strings ) ) {
			return;
		}

		foreach ( $targets as $lang ) {
			$lang = sanitize_key( $lang );
			if ( empty( $lang ) ) {
				continue;
			}

			foreach ( $shared_strings as $string ) {
				\TeknoTok\AiTranslator\Database\JobRepository::add_string_job( $string, $lang );
			}
		}
	}

	/**
	 * Queue translated frontend URL warmups so theme, menu, header, and footer strings are discovered.
	 *
	 * @param array $targets Target language codes.
	 * @return void
	 */
	private function queue_frontend_warmup_jobs_for_targets( $targets ) {
		$urls = $this->get_frontend_warmup_urls();
		$this->queue_frontend_shared_string_jobs_for_targets( $targets );

		if ( empty( $urls ) ) {
			return;
		}

		foreach ( $targets as $lang ) {
			$lang = sanitize_key( $lang );
			if ( empty( $lang ) ) {
				continue;
			}

			foreach ( $urls as $url ) {
				\TeknoTok\AiTranslator\Database\JobRepository::add_frontend_warmup_job( $url, $lang );
			}

			\TeknoTok\AiTranslator\Utils\Logger::log( sprintf( 'Queued %d frontend warmup URLs for target language %s.', count( $urls ), $lang ), 'info' );
		}
	}

	/**
	 * Collect representative shared frontend URLs to render after setup or target-language changes.
	 *
	 * @return array Local source-language URLs.
	 */
	private function get_frontend_warmup_urls() {
		$urls = array( home_url( '/' ) );
		$max_urls = (int) apply_filters( 'teknotok_ait_frontend_warmup_url_limit', 500 );
		$max_urls = max( 10, min( 500, $max_urls ) );

		if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
			$front_url = get_permalink( (int) get_option( 'page_on_front' ) );
			if ( $front_url ) {
				$urls[] = $front_url;
			}
		}

		if ( get_option( 'page_for_posts' ) ) {
			$posts_url = get_permalink( (int) get_option( 'page_for_posts' ) );
			if ( $posts_url ) {
				$urls[] = $posts_url;
			}
		}

		$post_types = $this->get_frontend_warmup_post_types();
		if ( ! empty( $post_types ) ) {
			global $wpdb;

			foreach ( $post_types as $post_type ) {
				$remaining = $max_urls - count( $urls );
				if ( $remaining <= 0 ) {
					break;
				}

				$post_ids = $wpdb->get_col(
					$wpdb->prepare(
						"SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_status = 'publish' ORDER BY ID DESC LIMIT %d",
						$post_type,
						$remaining
					)
				);

				if ( ! empty( $post_ids ) ) {
					foreach ( $post_ids as $post_id ) {
						if ( count( $urls ) >= $max_urls ) {
							break 2;
						}

						$permalink = get_permalink( (int) $post_id );
						if ( $permalink ) {
							$urls[] = $permalink;
						}
					}
				}
			}
		}

		$urls = array_filter( array_unique( $urls ), array( '\TeknoTok\AiTranslator\Utils\UrlGuard', 'is_frontend_url' ) );

		return array_values( $urls );
	}

	/**
	 * Get published post types whose frontend URLs should be rendered for warmup.
	 *
	 * @return array Post type names.
	 */
	private function get_frontend_warmup_post_types() {
		$post_types = array( 'post', 'page', 'product' );
		$public_post_types = get_post_types(
			array(
				'public' => true,
			),
			'names'
		);

		if ( is_array( $public_post_types ) ) {
			$post_types = array_merge( $post_types, array_values( $public_post_types ) );
		}

		$post_types = array_filter( array_unique( array_map( 'sanitize_key', $post_types ) ) );
		$post_types = array_filter(
			$post_types,
			function( $post_type ) {
				if ( 'attachment' === $post_type ) {
					return false;
				}

				return post_type_exists( $post_type ) && is_post_type_viewable( $post_type );
			}
		);

		return array_values( $post_types );
	}

	/**
	 * Collect known shared frontend strings without rendering every content page.
	 *
	 * @return array Shared source-language strings.
	 */
	private function get_frontend_shared_strings() {
		$strings = array(
			get_bloginfo( 'name' ),
			get_bloginfo( 'description' ),
		);

		$locations = get_nav_menu_locations();
		if ( ! empty( $locations ) ) {
			foreach ( $locations as $menu_id ) {
				$items = wp_get_nav_menu_items( $menu_id );
				if ( empty( $items ) || is_wp_error( $items ) ) {
					continue;
				}

				foreach ( $items as $item ) {
					if ( ! empty( $item->title ) ) {
						$strings[] = $item->title;
					}
				}
			}
		}

		$strings = array_merge( $strings, $this->get_woocommerce_checkout_shared_strings() );
		$strings = array_merge( $strings, $this->get_woocommerce_product_shared_strings() );
		$strings = array_merge( $strings, $this->get_woocommerce_shipping_shared_strings() );

		$strings = array_filter( array_unique( array_map( 'trim', $strings ) ) );

		return array_values( $strings );
	}

	/**
	 * Collect WooCommerce checkout strings that are public-facing but often rendered by blocks.
	 *
	 * @return array Checkout source-language strings.
	 */
	private function get_woocommerce_checkout_shared_strings() {
		$strings = array();

		if ( ! function_exists( 'WC' ) || ! WC() ) {
			return $strings;
		}

		if ( WC()->payment_gateways() ) {
			$gateways = WC()->payment_gateways()->payment_gateways();
			if ( is_array( $gateways ) ) {
				foreach ( $gateways as $gateway ) {
					if ( ! is_object( $gateway ) ) {
						continue;
					}

					foreach ( array( 'title', 'description', 'method_title', 'method_description' ) as $property ) {
						if ( isset( $gateway->{$property} ) && is_scalar( $gateway->{$property} ) ) {
							$strings[] = wp_strip_all_tags( (string) $gateway->{$property} );
						}
					}

					if ( method_exists( $gateway, 'get_title' ) ) {
						$strings[] = wp_strip_all_tags( (string) $gateway->get_title() );
					}
					if ( method_exists( $gateway, 'get_description' ) ) {
						$strings[] = wp_strip_all_tags( (string) $gateway->get_description() );
					}
				}
			}
		}

		if ( WC()->checkout() ) {
			$fields = WC()->checkout()->get_checkout_fields();
			if ( is_array( $fields ) ) {
				foreach ( $fields as $field_group ) {
					if ( ! is_array( $field_group ) ) {
						continue;
					}

					foreach ( $field_group as $field ) {
						if ( ! is_array( $field ) ) {
							continue;
						}

						foreach ( array( 'label', 'placeholder', 'description' ) as $text_key ) {
							if ( isset( $field[ $text_key ] ) && is_scalar( $field[ $text_key ] ) ) {
								$strings[] = wp_strip_all_tags( (string) $field[ $text_key ] );
							}
						}

						if ( ! empty( $field['options'] ) && is_array( $field['options'] ) ) {
							foreach ( $field['options'] as $option_label ) {
								if ( is_scalar( $option_label ) ) {
									$strings[] = wp_strip_all_tags( (string) $option_label );
								}
							}
						}
					}
				}
			}
		}

		$strings = array_merge( $strings, $this->get_woocommerce_blocks_checkout_field_strings() );

		return $strings;
	}

	/**
	 * Collect WooCommerce shipping and cart labels that often render through blocks or rate objects.
	 *
	 * @return array Shipping and cart source-language strings.
	 */
	private function get_woocommerce_shipping_shared_strings() {
		$strings = array(
			'Shipping',
			'Free shipping',
			'Flat rate',
			'Local pickup',
			'Subtotal',
			'Total',
			'Cart totals',
			'Estimated total',
			'Proceed to checkout',
			'Apply coupon',
		);

		if ( ! function_exists( 'WC' ) || ! WC() ) {
			return $strings;
		}

		if ( WC()->shipping() && method_exists( WC()->shipping(), 'get_shipping_methods' ) ) {
			$shipping_methods = WC()->shipping()->get_shipping_methods();
			if ( is_array( $shipping_methods ) ) {
				foreach ( $shipping_methods as $method ) {
					$strings = array_merge( $strings, $this->get_shipping_method_strings( $method ) );
				}
			}
		}

		if ( class_exists( '\WC_Shipping_Zones' ) ) {
			$zones = \WC_Shipping_Zones::get_zones();
			if ( is_array( $zones ) ) {
				foreach ( $zones as $zone ) {
					if ( empty( $zone['shipping_methods'] ) || ! is_array( $zone['shipping_methods'] ) ) {
						continue;
					}

					foreach ( $zone['shipping_methods'] as $method ) {
						$strings = array_merge( $strings, $this->get_shipping_method_strings( $method ) );
					}
				}
			}

			$default_zone = \WC_Shipping_Zones::get_zone_by( 'zone_id', 0 );
			if ( is_object( $default_zone ) && method_exists( $default_zone, 'get_shipping_methods' ) ) {
				$default_methods = $default_zone->get_shipping_methods();
				if ( is_array( $default_methods ) ) {
					foreach ( $default_methods as $method ) {
						$strings = array_merge( $strings, $this->get_shipping_method_strings( $method ) );
					}
				}
			}
		}

		return array_values( array_filter( array_unique( array_map( 'trim', $strings ) ) ) );
	}

	/**
	 * Collect display labels from one WooCommerce shipping method object.
	 *
	 * @param object $method Shipping method object.
	 * @return array Method source-language strings.
	 */
	private function get_shipping_method_strings( $method ) {
		if ( ! is_object( $method ) ) {
			return array();
		}

		$strings = array();
		foreach ( array( 'title', 'method_title', 'method_description' ) as $property ) {
			if ( isset( $method->{$property} ) && is_scalar( $method->{$property} ) ) {
				$strings[] = wp_strip_all_tags( (string) $method->{$property} );
			}
		}

		foreach ( array( 'get_title', 'get_method_title', 'get_method_description' ) as $method_name ) {
			if ( method_exists( $method, $method_name ) ) {
				$value = $method->{$method_name}();
				if ( is_scalar( $value ) ) {
					$strings[] = wp_strip_all_tags( (string) $value );
				}
			}
		}

		return $strings;
	}

	/**
	 * Collect common WooCommerce product-page labels that should be cached early.
	 *
	 * @return array Product-page source-language strings.
	 */
	private function get_woocommerce_product_shared_strings() {
		if ( ! class_exists( '\WooCommerce' ) && ! function_exists( 'WC' ) ) {
			return array();
		}

		return array(
			'Add to cart',
			'Category',
			'Category:',
			'Categories',
			'Categories:',
			'Description',
			'Reviews',
		);
	}

	/**
	 * Collect WooCommerce Blocks additional checkout field labels registered by any plugin.
	 *
	 * @return array Checkout field strings.
	 */
	private function get_woocommerce_blocks_checkout_field_strings() {
		$strings = array();

		if ( ! class_exists( '\Automattic\WooCommerce\Blocks\Package' ) || ! class_exists( '\Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields' ) ) {
			return $strings;
		}

		try {
			$controller = \Automattic\WooCommerce\Blocks\Package::container()->get( \Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields::class );
		} catch ( \Throwable $e ) {
			return $strings;
		}

		if ( ! is_object( $controller ) || ! method_exists( $controller, 'get_additional_fields' ) ) {
			return $strings;
		}

		$fields = $controller->get_additional_fields();
		if ( ! is_array( $fields ) ) {
			return $strings;
		}

		foreach ( $fields as $field ) {
			if ( ! is_array( $field ) ) {
				continue;
			}

			foreach ( array( 'label', 'optionalLabel' ) as $text_key ) {
				if ( isset( $field[ $text_key ] ) && is_scalar( $field[ $text_key ] ) ) {
					$strings[] = wp_strip_all_tags( (string) $field[ $text_key ] );
				}
			}

			if ( ! empty( $field['options'] ) && is_array( $field['options'] ) ) {
				foreach ( $field['options'] as $option ) {
					if ( is_array( $option ) && isset( $option['label'] ) && is_scalar( $option['label'] ) ) {
						$strings[] = wp_strip_all_tags( (string) $option['label'] );
					}
				}
			}
		}

		return $strings;
	}

	/**
	 * Check whether a warmup URL belongs to this WordPress site.
	 *
	 * @param string $url URL to inspect.
	 * @return bool True for local site URLs.
	 */
	private function is_local_frontend_warmup_url( $url ) {
		return \TeknoTok\AiTranslator\Utils\UrlGuard::is_frontend_url( $url );
	}

	/**
	 * Build the translated URL for a source-language URL.
	 *
	 * @param string $source_url  Source-language URL.
	 * @param string $target_lang Target language code.
	 * @return string Translated frontend URL.
	 */
	public static function get_translated_url_for_source( $source_url, $target_lang ) {
		$settings    = get_option( 'teknotok_ait_settings', array() );
		$source      = $settings['source_language'] ?? 'en';
		$targets     = $settings['target_languages'] ?? array();
		$target_lang = sanitize_key( $target_lang );

		if ( empty( $source_url ) || empty( $target_lang ) || $target_lang === $source ) {
			return $source_url;
		}

		if ( ! \TeknoTok\AiTranslator\Utils\UrlGuard::is_frontend_url( $source_url ) ) {
			return $source_url;
		}

		$home = rtrim( get_option( 'home' ), '/' );
		if ( 0 !== strpos( $source_url, $home ) ) {
			return $source_url;
		}

		$path = substr( $source_url, strlen( $home ) );
		$path = \TeknoTok\AiTranslator\Frontend\UrlManager::clean_path_from_language_prefix( $path, $targets );

		return $home . '/' . $target_lang . '/' . ltrim( $path, '/' );
	}

	/**
	 * Handle Setup wizard request.
	 *
	 * @return void
	 */
	public function handle_setup_action() {
		if ( ! is_admin() || ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$setup_action = sanitize_key( wp_unslash( $_POST['teknotok_ait_setup_action'] ?? '' ) );
		if ( 'save_setup' !== $setup_action ) {
			return;
		}

		check_admin_referer( 'teknotok_ait_setup_wizard' );

		$target = sanitize_key( wp_unslash( $_POST['target_language'] ?? '' ) );
		if ( ! empty( $target ) ) {
			// Save default settings
			$settings = get_option( 'teknotok_ait_settings', array() );
			$settings['api_provider']     = 'teknotok';
			$settings['source_language']  = substr( get_locale(), 0, 2 );
			$settings['target_languages'] = array( $target );
			$settings['switcher_position']= 'floating-bottom-right';
			$settings['detect_browser']   = '1';
			$settings['auto_translate']   = '1';
			$settings['url_mode']         = 'subfolder';
			$settings['gateway_base_url'] = 'https://soft.teknotok.com';
			$settings['translation_tone'] = 'standard';
			$settings['queue_batch_size'] = 10;
			$settings['delete_data_on_uninstall'] = '0';

			update_option( 'teknotok_ait_settings', $settings, false );

			$this->queue_existing_content_for_targets( array( $target ) );

			// Trigger language pack download and warming up the queue for this target language
			if ( function_exists( 'as_enqueue_async_action' ) && ! \TeknoTok\AiTranslator\Queue\QueueManager::is_sqlite_active() ) {
				as_enqueue_async_action( 'teknotok_ait_download_translations', array( array( $target ) ), 'teknotok-ait' );
			} else {
				wp_schedule_single_event( time(), 'teknotok_ait_download_translations', array( array( $target ) ) );
				$site_url = get_site_url();
				$is_localhost = ( strpos( $site_url, 'localhost' ) !== false || strpos( $site_url, '127.0.0.1' ) !== false );
				if ( ! $is_localhost ) {
					spawn_cron();
				}
			}
		}

		// Mark setup as complete
		update_option( 'teknotok_ait_setup_complete', '1', false );

		// Redirect to diagnostics screen with setup parameters
		wp_safe_redirect( admin_url( 'admin.php?page=teknotok-ait-diagnostics&setup=start' ) );
		exit;
	}

	/**
	 * Check if the plugin has an active premium license and is fully configured.
	 *
	 * @return bool True if licensed and configured, false otherwise.
	 */
	public static function is_ready_for_translations() {
		static $is_ready = null;
		if ( null !== $is_ready ) {
			return $is_ready;
		}

		try {
			$provider = Providers\ProviderFactory::get_provider();
			$is_ready = $provider->is_configured();
		} catch ( \Exception $e ) {
			$is_ready = false;
		}

		return $is_ready;
	}

	/**
	 * Get list of supported languages.
	 *
	 * @return array
	 */
	public static function get_supported_languages() {
		return array(
			'en'  => __( 'English', 'teknotok-ai-translator' ),
			'es'  => __( 'Spanish', 'teknotok-ai-translator' ),
			'tr'  => __( 'Turkish', 'teknotok-ai-translator' ),
			'zh'  => __( 'Chinese', 'teknotok-ai-translator' ),
			'hi'  => __( 'Hindi', 'teknotok-ai-translator' ),
			'ar'  => __( 'Arabic', 'teknotok-ai-translator' ),
			'pt'  => __( 'Portuguese', 'teknotok-ai-translator' ),
			'bn'  => __( 'Bengali', 'teknotok-ai-translator' ),
			'ru'  => __( 'Russian', 'teknotok-ai-translator' ),
			'ja'  => __( 'Japanese', 'teknotok-ai-translator' ),
			'fr'  => __( 'French', 'teknotok-ai-translator' ),
			'de'  => __( 'German', 'teknotok-ai-translator' ),
			'it'  => __( 'Italian', 'teknotok-ai-translator' ),
			'nl'  => __( 'Dutch', 'teknotok-ai-translator' ),
			'pl'  => __( 'Polish', 'teknotok-ai-translator' ),
			'sv'  => __( 'Swedish', 'teknotok-ai-translator' ),
			'vi'  => __( 'Vietnamese', 'teknotok-ai-translator' ),
			'ko'  => __( 'Korean', 'teknotok-ai-translator' ),
			'id'  => __( 'Indonesian', 'teknotok-ai-translator' ),
			'el'  => __( 'Greek', 'teknotok-ai-translator' ),
			'da'  => __( 'Danish', 'teknotok-ai-translator' ),
			'fi'  => __( 'Finnish', 'teknotok-ai-translator' ),
			'no'  => __( 'Norwegian', 'teknotok-ai-translator' ),
			'cs'  => __( 'Czech', 'teknotok-ai-translator' ),
			'hu'  => __( 'Hungarian', 'teknotok-ai-translator' ),
			'ro'  => __( 'Romanian', 'teknotok-ai-translator' ),
			'sk'  => __( 'Slovak', 'teknotok-ai-translator' ),
			'uk'  => __( 'Ukrainian', 'teknotok-ai-translator' ),
			'bg'  => __( 'Bulgarian', 'teknotok-ai-translator' ),
			'hr'  => __( 'Croatian', 'teknotok-ai-translator' ),
			'sr'  => __( 'Serbian', 'teknotok-ai-translator' ),
			'sl'  => __( 'Slovenian', 'teknotok-ai-translator' ),
			'et'  => __( 'Estonian', 'teknotok-ai-translator' ),
			'lv'  => __( 'Latvian', 'teknotok-ai-translator' ),
			'lt'  => __( 'Lithuanian', 'teknotok-ai-translator' ),
			'th'  => __( 'Thai', 'teknotok-ai-translator' ),
			'ms'  => __( 'Malay', 'teknotok-ai-translator' ),
			'fa'  => __( 'Persian', 'teknotok-ai-translator' ),
			'ur'  => __( 'Urdu', 'teknotok-ai-translator' ),
			'ta'  => __( 'Tamil', 'teknotok-ai-translator' ),
			'te'  => __( 'Telugu', 'teknotok-ai-translator' ),
			'ml'  => __( 'Malayalam', 'teknotok-ai-translator' ),
			'kn'  => __( 'Kannada', 'teknotok-ai-translator' ),
			'mr'  => __( 'Marathi', 'teknotok-ai-translator' ),
			'gu'  => __( 'Gujarati', 'teknotok-ai-translator' ),
			'pa'  => __( 'Punjabi', 'teknotok-ai-translator' ),
			'sw'  => __( 'Swahili', 'teknotok-ai-translator' ),
			'af'  => __( 'Afrikaans', 'teknotok-ai-translator' ),
			'sq'  => __( 'Albanian', 'teknotok-ai-translator' ),
			'am'  => __( 'Amharic', 'teknotok-ai-translator' ),
			'hy'  => __( 'Armenian', 'teknotok-ai-translator' ),
			'az'  => __( 'Azerbaijani', 'teknotok-ai-translator' ),
			'eu'  => __( 'Basque', 'teknotok-ai-translator' ),
			'be'  => __( 'Belarusian', 'teknotok-ai-translator' ),
			'bs'  => __( 'Bosnian', 'teknotok-ai-translator' ),
			'ca'  => __( 'Catalan', 'teknotok-ai-translator' ),
			'ceb' => __( 'Cebuano', 'teknotok-ai-translator' ),
			'ny'  => __( 'Chichewa', 'teknotok-ai-translator' ),
			'co'  => __( 'Corsican', 'teknotok-ai-translator' ),
			'eo'  => __( 'Esperanto', 'teknotok-ai-translator' ),
			'fy'  => __( 'Frisian', 'teknotok-ai-translator' ),
			'gl'  => __( 'Galician', 'teknotok-ai-translator' ),
			'ka'  => __( 'Georgian', 'teknotok-ai-translator' ),
			'haw' => __( 'Hawaiian', 'teknotok-ai-translator' ),
			'hmn' => __( 'Hmong', 'teknotok-ai-translator' ),
			'is'  => __( 'Icelandic', 'teknotok-ai-translator' ),
			'ig'  => __( 'Igbo', 'teknotok-ai-translator' ),
			'ga'  => __( 'Irish', 'teknotok-ai-translator' ),
			'jv'  => __( 'Javanese', 'teknotok-ai-translator' ),
			'kk'  => __( 'Kazakh', 'teknotok-ai-translator' ),
			'km'  => __( 'Khmer', 'teknotok-ai-translator' ),
			'ku'  => __( 'Kurdish', 'teknotok-ai-translator' ),
			'ky'  => __( 'Kyrgyz', 'teknotok-ai-translator' ),
			'lo'  => __( 'Lao', 'teknotok-ai-translator' ),
			'la'  => __( 'Latin', 'teknotok-ai-translator' ),
			'lb'  => __( 'Luxembourgish', 'teknotok-ai-translator' ),
			'mk'  => __( 'Macedonian', 'teknotok-ai-translator' ),
			'mg'  => __( 'Malagasy', 'teknotok-ai-translator' ),
			'mi'  => __( 'Maori', 'teknotok-ai-translator' ),
			'mn'  => __( 'Mongolian', 'teknotok-ai-translator' ),
			'my'  => __( 'Myanmar (Burmese)', 'teknotok-ai-translator' ),
			'ne'  => __( 'Nepali', 'teknotok-ai-translator' ),
			'ps'  => __( 'Pashto', 'teknotok-ai-translator' ),
			'sm'  => __( 'Samoan', 'teknotok-ai-translator' ),
			'gd'  => __( 'Scots Gaelic', 'teknotok-ai-translator' ),
			'st'  => __( 'Sesotho', 'teknotok-ai-translator' ),
			'sn'  => __( 'Shona', 'teknotok-ai-translator' ),
			'sd'  => __( 'Sindhi', 'teknotok-ai-translator' ),
			'si'  => __( 'Sinhala', 'teknotok-ai-translator' ),
			'so'  => __( 'Somali', 'teknotok-ai-translator' ),
			'su'  => __( 'Sundanese', 'teknotok-ai-translator' ),
			'tg'  => __( 'Tajik', 'teknotok-ai-translator' ),
			'tt'  => __( 'Tatar', 'teknotok-ai-translator' ),
			'uz'  => __( 'Uzbek', 'teknotok-ai-translator' ),
			'cy'  => __( 'Welsh', 'teknotok-ai-translator' ),
			'xh'  => __( 'Xhosa', 'teknotok-ai-translator' ),
			'yi'  => __( 'Yiddish', 'teknotok-ai-translator' ),
			'yo'  => __( 'Yoruba', 'teknotok-ai-translator' ),
			'zu'  => __( 'Zulu', 'teknotok-ai-translator' ),
		);
	}

	/**
	 * Get supported languages sorted for customer-facing controls.
	 *
	 * @return array Language code to display name map.
	 */
	public static function get_supported_languages_by_priority() {
		return self::sort_language_map_by_priority( self::get_supported_languages() );
	}

	/**
	 * Sort a language map by customer-facing importance, then by language name.
	 *
	 * @param array $languages Language code to display name map.
	 * @return array Sorted language map.
	 */
	public static function sort_language_map_by_priority( $languages ) {
		$sorted_codes = self::sort_language_codes_by_priority( array_keys( (array) $languages ), $languages );
		$sorted       = array();

		foreach ( $sorted_codes as $code ) {
			if ( isset( $languages[ $code ] ) ) {
				$sorted[ $code ] = $languages[ $code ];
			}
		}

		return $sorted;
	}

	/**
	 * Sort language codes by customer-facing importance, then by language name.
	 *
	 * @param array $codes     Language codes.
	 * @param array $languages Optional language code to display name map.
	 * @return array Sorted language codes.
	 */
	public static function sort_language_codes_by_priority( $codes, $languages = array() ) {
		$priority = array_flip( self::get_language_priority_order() );
		$codes    = array_values( array_unique( array_filter( array_map( 'sanitize_key', (array) $codes ) ) ) );

		usort(
			$codes,
			function( $left, $right ) use ( $priority, $languages ) {
				$left_index  = $priority[ $left ] ?? PHP_INT_MAX;
				$right_index = $priority[ $right ] ?? PHP_INT_MAX;

				if ( $left_index !== $right_index ) {
					return $left_index <=> $right_index;
				}

				return strcasecmp( $languages[ $left ] ?? $left, $languages[ $right ] ?? $right );
			}
		);

		return $codes;
	}

	/**
	 * Normalize selected target languages for storage and runtime use.
	 *
	 * @param array       $targets    Target language codes.
	 * @param string      $source     Source language code.
	 * @param bool|null   $is_premium Whether the store has premium access.
	 * @return array Normalized target language codes.
	 */
	public static function normalize_target_languages( $targets, $source = 'en', $is_premium = null ) {
		$source          = sanitize_key( $source );
		$supported_codes = self::get_supported_language_codes();
		$targets         = array_values( array_unique( array_filter( array_map( 'sanitize_key', (array) $targets ) ) ) );
		$targets         = array_values( array_intersect( $targets, $supported_codes ) );
		$targets         = array_values(
			array_filter(
				$targets,
				function( $target ) use ( $source ) {
					return $target !== $source;
				}
			)
		);
		$targets         = self::sort_language_codes_by_priority( $targets );

		return array_slice( $targets, 0, self::get_max_target_languages( $is_premium ) );
	}

	/**
	 * Get the maximum target-language count for the current or supplied license state.
	 *
	 * @param bool|null $is_premium Whether the store has premium access.
	 * @return int
	 */
	public static function get_max_target_languages( $is_premium = null ) {
		if ( null === $is_premium ) {
			$is_premium = Integrations\LicenseManager::is_premium_active();
		}

		return $is_premium ? self::MAX_PREMIUM_TARGET_LANGUAGES : self::MAX_FREE_TARGET_LANGUAGES;
	}

	/**
	 * Top worldwide languages that should always be present and prioritized.
	 *
	 * @return array Language codes.
	 */
	public static function get_most_used_language_codes() {
		return array( 'en', 'zh', 'hi', 'es', 'fr', 'ar', 'bn', 'pt', 'ru', 'ur' );
	}

	/**
	 * Customer-facing language priority for admin and switcher controls.
	 *
	 * @return array Language codes.
	 */
	public static function get_language_priority_order() {
		return array_merge(
			self::get_most_used_language_codes(),
			array(
				'id', 'de', 'ja', 'tr', 'ko', 'vi', 'it', 'fa', 'pl', 'uk', 'nl',
				'th', 'ms', 'ta', 'te', 'mr', 'gu', 'pa', 'sw', 'el', 'da', 'fi',
				'no', 'cs', 'hu', 'ro', 'sk', 'bg', 'hr', 'sr', 'sl', 'et', 'lv',
				'lt', 'af', 'sq', 'am', 'hy', 'az', 'eu', 'be', 'bs', 'ca', 'ceb',
				'ny', 'co', 'eo', 'fy', 'gl', 'ka', 'haw', 'hmn', 'is', 'ig', 'ga',
				'jv', 'kk', 'km', 'ku', 'ky', 'lo', 'la', 'lb', 'mk', 'mg', 'mi',
				'mn', 'my', 'ne', 'ps', 'sm', 'gd', 'st', 'sn', 'sd', 'si', 'so',
				'su', 'tg', 'tt', 'uz', 'cy', 'xh', 'yi', 'yo', 'zu',
			)
		);
	}

	/**
	 * Get mapping of ISO 639-1 language code to WordPress locale.
	 *
	 * @param string $lang Language code.
	 * @return string WordPress locale.
	 */
	public static function get_locale_for_lang( $lang ) {
		$locales_map = array(
			'en'  => 'en_US',
			'es'  => 'es_ES',
			'tr'  => 'tr_TR',
			'zh'  => 'zh_CN',
			'hi'  => 'hi_IN',
			'ar'  => 'ar',
			'pt'  => 'pt_BR',
			'bn'  => 'bn_BD',
			'ru'  => 'ru_RU',
			'ja'  => 'ja',
			'fr'  => 'fr_FR',
			'de'  => 'de_DE',
			'it'  => 'it_IT',
			'nl'  => 'nl_NL',
			'pl'  => 'pl_PL',
			'sv'  => 'sv_SE',
			'vi'  => 'vi_VN',
			'ko'  => 'ko_KR',
			'id'  => 'id_ID',
			'el'  => 'el_GR',
			'da'  => 'da_DK',
			'fi'  => 'fi_FI',
			'no'  => 'nb_NO',
			'cs'  => 'cs_CZ',
			'hu'  => 'hu_HU',
			'ro'  => 'ro_RO',
			'sk'  => 'sk_SK',
			'uk'  => 'uk_UA',
			'bg'  => 'bg_BG',
			'hr'  => 'hr_HR',
			'sr'  => 'sr_RS',
			'sl'  => 'sl_SI',
			'et'  => 'et_EE',
			'lv'  => 'lv_LV',
			'lt'  => 'lt_LT',
			'th'  => 'th_TH',
			'ms'  => 'ms_MY',
			'fa'  => 'fa_IR',
			'ur'  => 'ur_PK',
			'ta'  => 'ta_IN',
			'te'  => 'te_IN',
			'ml'  => 'ml_IN',
			'kn'  => 'kn_IN',
			'mr'  => 'mr_IN',
			'gu'  => 'gu_IN',
			'pa'  => 'pa_IN',
			'sw'  => 'sw_KE',
			'af'  => 'af_ZA',
			'sq'  => 'sq_AL',
			'am'  => 'am_ET',
			'hy'  => 'hy_AM',
			'az'  => 'az_AZ',
			'eu'  => 'eu_ES',
			'be'  => 'be_BY',
			'bs'  => 'bs_BA',
			'ca'  => 'ca_ES',
			'co'  => 'co_FR',
			'eo'  => 'eo',
			'fy'  => 'fy_NL',
			'gl'  => 'gl_ES',
			'ka'  => 'ka_GE',
			'is'  => 'is_IS',
			'ig'  => 'ig_NG',
			'ga'  => 'ga_IE',
			'jv'  => 'jv_ID',
			'kk'  => 'kk_KZ',
			'km'  => 'km_KH',
			'ku'  => 'ku_TR',
			'ky'  => 'ky_KG',
			'lo'  => 'lo_LA',
			'la'  => 'la',
			'lb'  => 'lb_LU',
			'mk'  => 'mk_MK',
			'mg'  => 'mg_MG',
			'mi'  => 'mi_NZ',
			'mn'  => 'mn_MN',
			'my'  => 'my_MM',
			'ne'  => 'ne_NP',
			'ps'  => 'ps_AF',
			'sm'  => 'sm',
			'gd'  => 'gd_GB',
			'st'  => 'st_ZA',
			'sn'  => 'sn_ZW',
			'sd'  => 'sd_PK',
			'si'  => 'si_LK',
			'so'  => 'so_SO',
			'su'  => 'su_ID',
			'tg'  => 'tg_TJ',
			'tt'  => 'tt_RU',
			'uz'  => 'uz_UZ',
			'cy'  => 'cy_GB',
			'xh'  => 'xh_ZA',
			'yi'  => 'yi',
			'yo'  => 'yo_NG',
			'zu'  => 'zu_ZA',
		);
		return $locales_map[ $lang ] ?? $lang;
	}

	/**
	 * Filter settings option value on retrieval to strip out unsupported target languages.
	 *
	 * @param mixed $value Settings array.
	 * @return mixed Sanitized settings array.
	 */
	public function sanitize_settings_option( $value ) {
		if ( is_array( $value ) && isset( $value['target_languages'] ) && is_array( $value['target_languages'] ) ) {
			$value['target_languages'] = self::normalize_target_languages(
				$value['target_languages'],
				$value['source_language'] ?? 'en'
			);
		}
		return $value;
	}

	/**
	 * Get simple array of supported language codes to prevent translation function recursion.
	 *
	 * @return array List of supported ISO 639-1 language codes.
	 */
	public static function get_supported_language_codes() {
		return array(
			'en', 'es', 'tr', 'zh', 'hi', 'ar', 'pt', 'bn', 'ru', 'ja', 'fr', 'de', 'it', 'nl', 'pl', 'sv', 'vi', 'ko', 'id', 'el', 'da', 'fi', 'no', 'cs', 'hu', 'ro', 'sk', 'uk', 'bg', 'hr', 'sr', 'sl', 'et', 'lv', 'lt', 'th', 'ms', 'fa', 'ur', 'ta', 'te', 'ml', 'kn', 'mr', 'gu', 'pa', 'sw', 'af', 'sq', 'am', 'hy', 'az', 'eu', 'be', 'bs', 'ca', 'ceb', 'ny', 'co', 'eo', 'fy', 'gl', 'ka', 'haw', 'hmn', 'is', 'ig', 'ga', 'jv', 'kk', 'km', 'ku', 'ky', 'lo', 'la', 'lb', 'mk', 'mg', 'mi', 'mn', 'my', 'ne', 'ps', 'sm', 'gd', 'st', 'sn', 'sd', 'si', 'so', 'su', 'tg', 'tt', 'uz', 'cy', 'xh', 'yi', 'yo', 'zu'
		);
	}
}
