<?php

namespace Limb_Chatbot\Includes\Translations;

use Limb_Chatbot\Includes\Traits\SingletonTrait;

/**
 * Loads and merges multiple script translation JSON files so bundled scripts
 * get all relevant translations. Works with any translation plugin that
 * generates one JSON per source path (e.g. Loco, wp i18n make-json).
 *
 * - Public script (lbaic-chatbot): merges JSONs whose "source" is under assets/js/
 *   but not under assets/js/admin/.
 * - Admin scripts: merge all JSONs whose "source" is under assets/js/.
 *
 * @since 1.0.0
 */
class Script_Translations {

	use SingletonTrait;

	/** Public frontend script handle. */
	private const PUBLIC_HANDLE = 'lbaic-chatbot';

	/** Admin script handles that receive all JS translations. */
	private const ADMIN_HANDLES = array(
		'lbaic-page-settings-script',
		'lbaic-page-settings-chatbot-script',
	);

	/**
	 * Initialize: register the filter.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function init(): void {
		add_filter( 'pre_load_script_translations', array( $this, 'serve_merged_json' ), 10, 4 );
	}

	/**
	 * If this is our script and domain, load and merge the right JSON files and return one JSON.
	 *
	 * @param string|false|null $translations JSON string or null to use default loading.
	 * @param string|false $file Path to the JSON file WordPress would load.
	 * @param string $handle Script handle.
	 * @param string $domain Text domain.
	 *
	 * @return string|null JSON-encoded Jed data, or null to let WordPress load the file.
	 */
	public function serve_merged_json( $translations, $file, $handle, $domain ) {
		if ( ! is_string( $domain ) || $domain === '' || $domain !== 'limb-chatbot' ) {
			return null;
		}
		if ( ! is_string( $handle ) || $handle === '' ) {
			return null;
		}
		$is_public = $handle === self::PUBLIC_HANDLE;
		$is_admin  = is_array( self::ADMIN_HANDLES ) && in_array( $handle, self::ADMIN_HANDLES, true );
		if ( ! $is_public && ! $is_admin ) {
			return null;
		}

		$locale = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
		$locale = is_string( $locale ) ? $locale : '';
		if ( $locale === '' ) {
			return null;
		}

		$dirs = $this->get_translation_dirs( $file );
		if ( ! is_array( $dirs ) ) {
			return null;
		}

		$files   = array();
		$locales = $this->get_locale_fallbacks( $locale );
		if ( ! is_array( $locales ) ) {
			return null;
		}

		foreach ( $dirs as $dir ) {
			if ( ! is_string( $dir ) || $dir === '' || ! is_dir( $dir ) ) {
				continue;
			}
			foreach ( $locales as $loc ) {
				if ( ! is_string( $loc ) || $loc === '' ) {
					continue;
				}
				$pattern = $dir . '/limb-chatbot-' . $loc . '-*.json';
				$found   = glob( $pattern );
				if ( is_array( $found ) ) {
					$files = array_merge( $files, $found );
				}
			}
		}

		$files = is_array( $files ) ? array_values( array_unique( $files ) ) : array();
		if ( empty( $files ) ) {
			return null;
		}

		$merged = $this->merge_json_files( $files, $is_public );
		if ( ! is_array( $merged ) ) {
			return null;
		}
		$json = wp_json_encode( $merged, JSON_UNESCAPED_UNICODE );

		return is_string( $json ) && $json !== '' ? $json : null;
	}

	/**
	 * Directories to search for limb-chatbot-{locale}-*.json (plugin + relocated, e.g. Loco).
	 *
	 * When users relocate translations, JSONs may live in:
	 * - Plugin languages folder (default)
	 * - WP_LANG_DIR/plugins (WordPress standard)
	 * - WP_LANG_DIR/loco/plugins (Loco "Custom" location, e.g. .../languages/loco/plugins/limb-chatbot-nl_NL.po)
	 *
	 * @param string|false $file Path to the JSON file WordPress would load (from wp_set_script_translations or WP_LANG_DIR).
	 *
	 * @return list<string>
	 */
	private function get_translation_dirs( $file ): array {
		$dirs = array();
		if ( is_string( $file ) && $file !== '' ) {
			$dir = dirname( $file );
			if ( is_string( $dir ) && $dir !== '.' && $dir !== '' ) {
				$dirs[] = $dir;
			}
		}
		if ( defined( 'LIMB_CHATBOT_FILE' ) && LIMB_CHATBOT_FILE ) {
			$plugin_base = plugin_dir_path( LIMB_CHATBOT_FILE );
			if ( is_string( $plugin_base ) && $plugin_base !== '' ) {
				$plugin_lang = $plugin_base . 'languages';
				if ( is_string( $plugin_lang ) && ! in_array( $plugin_lang, $dirs, true ) ) {
					$dirs[] = $plugin_lang;
				}
			}
		}
		if ( defined( 'WP_LANG_DIR' ) && is_string( WP_LANG_DIR ) && WP_LANG_DIR !== '' ) {
			$global_plugins = WP_LANG_DIR . '/plugins';
			if ( ! in_array( $global_plugins, $dirs, true ) ) {
				$dirs[] = $global_plugins;
			}
			$loco_custom = WP_LANG_DIR . '/loco/plugins';
			if ( ! in_array( $loco_custom, $dirs, true ) ) {
				$dirs[] = $loco_custom;
			}
		}
		$dirs = array_unique( $dirs );

		return is_array( $dirs ) ? array_values( $dirs ) : array();
	}

	/**
	 * Locale variants to try when globbing for JSONs (fixes e.g. site locale "sl" vs Loco "sl_SI").
	 *
	 * @param string $locale Current locale from determine_locale().
	 *
	 * @return list<string>
	 */
	private function get_locale_fallbacks( string $locale ): array {
		$locale = is_string( $locale ) ? trim( $locale ) : '';
		if ( $locale === '' ) {
			return array();
		}
		$list = array( $locale );
		// If locale has a region (e.g. sl_SI), also try language only (sl).
		if ( strpos( $locale, '_' ) !== false ) {
			$lang = strstr( $locale, '_', true );
			if ( is_string( $lang ) && $lang !== '' && ! in_array( $lang, $list, true ) ) {
				$list[] = $lang;
			}
		} else {
			$lang_to_region = array(
				'sl' => 'sl_SI',
				'nl' => 'nl_NL',
				'de' => 'de_DE',
				'fr' => 'fr_FR',
				'es' => 'es_ES',
				'pt' => 'pt_PT',
				'it' => 'it_IT',
				'pl' => 'pl_PL',
				'ru' => 'ru_RU',
			);
			if ( isset( $lang_to_region[ $locale ] ) && is_string( $lang_to_region[ $locale ] ) && ! in_array( $lang_to_region[ $locale ], $list, true ) ) {
				$list[] = $lang_to_region[ $locale ];
			}
		}
		$list = array_unique( $list );

		return is_array( $list ) ? array_values( $list ) : array();
	}

	/**
	 * Merge JSON files: include only those matching public vs admin rule, then merge locale_data.
	 *
	 * @param list<string> $files Paths to JSON files.
	 * @param bool $public_only True = only sources under assets/js/ and NOT assets/js/admin/.
	 *
	 * @return array|null Jed structure (locale_data, etc.) or null if nothing to merge.
	 */
	private function merge_json_files( array $files, bool $public_only ): ?array {
		if ( ! is_array( $files ) ) {
			return null;
		}
		$messages  = array();
		$headers   = null;
		$domain    = 'limb-chatbot';
		$assets_js = 'assets/js/';

		foreach ( $files as $path ) {
			if ( ! is_string( $path ) || $path === '' ) {
				continue;
			}
			if ( ! is_readable( $path ) ) {
				continue;
			}
			$content = file_get_contents( $path );
			if ( $content === false || ! is_string( $content ) ) {
				continue;
			}
			$jed = json_decode( $content, true );
			if ( ! is_array( $jed ) || ! isset( $jed['locale_data'] ) || ! is_array( $jed['locale_data'] ) ) {
				continue;
			}
			$source = isset( $jed['source'] ) && is_string( $jed['source'] ) ? $jed['source'] : '';
			if ( $source === '' || strpos( $source, '.js' ) === false ) {
				continue;
			}
			if ( strpos( $source, $assets_js ) !== 0 ) {
				continue;
			}
			if ( $public_only && strpos( $source, 'assets/js/admin/' ) === 0 ) {
				continue;
			}

			foreach ( $jed['locale_data'] as $dom => $data ) {
				if ( ! is_array( $data ) ) {
					continue;
				}
				foreach ( $data as $msgid => $arr ) {
					if ( $msgid === '' ) {
						if ( $headers === null && is_array( $arr ) ) {
							$headers = $arr;
						}
						continue;
					}
					if ( ! is_array( $arr ) || ! isset( $arr[0] ) ) {
						continue;
					}
					$key              = is_scalar( $msgid ) ? $msgid : (string) $msgid;
					$messages[ $key ] = $arr;
				}
			}
		}

		if ( empty( $messages ) || ! is_array( $messages ) ) {
			return null;
		}
		if ( $headers === null || ! is_array( $headers ) ) {
			$locale_for_header = function_exists( 'determine_locale' ) ? determine_locale() : get_locale();
			$locale_for_header = is_string( $locale_for_header ) ? $locale_for_header : 'en';
			$headers           = array(
				'domain'       => $domain,
				'lang'         => str_replace( '_', '-', $locale_for_header ),
				'plural-forms' => 'nplurals=2; plural=n != 1;',
			);
		}
		$data = array( '' => $headers ) + $messages;

		return array(
			'translation-revision-date' => gmdate( 'Y-m-d H:iO' ),
			'generator'                 => 'Limb Chatbot',
			'source'                    => '',
			'domain'                    => $domain,
			'locale_data'               => array( $domain => $data ),
		);
	}
}
