<?php
declare( strict_types=1 );

namespace FiboFilters\Util;

use FiboFilters\Config\Env;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Localization
 */
class Localization {
	public function get_front_translations(): array {
		$locale       = determine_locale();
		$file_path    = $this->determine_translation_file( $locale );
		$translations = [];

		if ( empty( $file_path ) ) {
			$fallback_locale = $this->get_fallback_locale( $locale );

			if ( ! empty( $fallback_locale ) ) {
				$file_path = $this->determine_translation_file( $fallback_locale );
				// Replace the locale with the fallback locale if the translation file exist.
				if ( ! empty( $file_path ) ) {
					$locale = $fallback_locale;
				}
			}
		}

		// If the translation file for the current/fallback locale does not exist use file with default locale translations.
		// This is necessary to allow overriding translations with the "fibofilters/i18n/front_translations" filter.
		if ( empty( $file_path ) ) {
			$file_path = $this->determine_translation_file( 'en_US' );
		}

		if ( ! empty( $file_path ) ) {
			$translations = $this->get_translations_from_file( $file_path );
		}

		$translations = apply_filters( 'fibofilters/i18n/front_translations', $translations, $locale );

		return $this->standardize_front_translations( $translations );
	}

	private function get_fallback_locale( string $locale ): string {
		$locales = [
			'de_AT' => 'de_DE',
			'de_CH' => 'de_DE',
			'es_AR' => 'es_ES',
			'es_CL' => 'es_ES',
			'es_CR' => 'es_ES',
			'es_CO' => 'es_ES',
			'es_DO' => 'es_ES',
			'es_EC' => 'es_ES',
			'es_GT' => 'es_ES',
			'es_HN' => 'es_ES',
			'es_MX' => 'es_ES',
			'es_PE' => 'es_ES',
			'es_PR' => 'es_ES',
			'es_UY' => 'es_ES',
			'es_VE' => 'es_ES',
			'fr_BE' => 'fr_FR',
			'fr_CA' => 'fr_FR',
			'nl_BE' => 'nl_NL',
			'pt_AO' => 'pt_PT',
			'pt_BR' => 'pt_PT',
			'zh_HK' => 'zh_CN',
			'zh_SG' => 'zh_CN',
			'zh_TW' => 'zh_CN',
		];

		$fallback_locale = $locales[ $locale ] ?? '';

		return (string) apply_filters( 'fibofilters/i18n/fallback_locale', $fallback_locale, $locale );
	}

	private function determine_translation_file( string $locale ): string {
		$file_path_loco   = sprintf( '%s/fibofilters-%s-%s.json', WP_LANG_DIR . DIRECTORY_SEPARATOR . 'loco' . DIRECTORY_SEPARATOR . 'plugins', $locale, md5( 'build/front-pro/front.js' ) );
		$file_path_wp     = sprintf( '%s/fibofilters-%s-%s.json', WP_LANG_DIR . DIRECTORY_SEPARATOR . 'plugins', $locale, md5( 'build/front-pro/front.js' ) );
		$file_path_plugin = sprintf( '%s/fibofilters-%s-%s.json', Env::$languages_path, $locale, md5( 'build/front-pro/front.js' ) );

		if ( file_exists( $file_path_loco ) ) {
			return $file_path_loco;
		} elseif ( file_exists( $file_path_wp ) ) {
			return $file_path_wp;
		} elseif ( file_exists( $file_path_plugin ) ) {
			return $file_path_plugin;
		}

		return '';
	}

	/**
	 * Read translations from JSON file
	 *
	 * @param string $file_path File path.
	 */
	public function get_translations_from_file( string $file_path ): array {
		// phpcs:disable WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
		$file_content = file_get_contents( $file_path );
		if ( $file_content === false ) {
			return [];
		}

		$json_data = json_decode( $file_content, true );
		if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $json_data ) ) {
			return [];
		}

		// Loco Translate uses the domain as the key in the translation JSON file.
		if ( isset( $json_data['locale_data']['fibofilters'] ) ) {
			return $json_data['locale_data']['fibofilters'];
		}

		// The default key where the translations are in the JSON file.
		if ( isset( $json_data['locale_data']['messages'] ) ) {
			return $json_data['locale_data']['messages'];
		}

		return [];
	}

	/**
	 * Normalizes a translation key by removing "[front]" prefixes, converting to uppercase,
	 * replacing certain characters, and formatting it into a standardized key format.
	 *
	 * - Removes "[front]" and optional control character after it.
	 * - Converts the string to uppercase.
	 * - Replaces spaces with underscores.
	 * - Removes dots.
	 * - Replaces control characters (ASCII EOT `\x04`) with double underscores.
	 * - Removes HTML tags.
	 * - Applying specific replacements for predefined key patterns.
	 *
	 *  input: [front] Number e.g. more than 49more than %s
	 *  output: NUMBER__MORE_THAN_%S
	 *
	 * @param string $key The original translation key.
	 *
	 * @return string The normalized translation key.
	 */
	public function normalize_translation_key( string $key ): string {
		$key = str_replace(
			[ ' ', '.', '', '\x04' ],
			[ '_', '', '__', '__' ],
			strip_tags( // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags
				strtoupper(
					preg_replace( '/\[front].?/u', '', $key )
				)
			)
		);

		$replacements = [
			'USED_IN_PRICE_RANGE_EG_FROM_$20_TO_$50' => 'PRICE_RANGE',
			'USED_IN_NUMBER_RANGE_EG_FROM_20_TO_50'  => 'NUMBER_RANGE',
			'SORTING_OF_PRODUCTS_IN_THE_SHOP'        => 'SORTING',
			'ABBREVIATION_FOR_MAXIMUM_RELATED_TO_'   => '',
			'ABBREVIATION_FOR_MINIMAL_RELATED_TO_'   => '',
			'_EG_LESS_THAN_49'                       => '',
			'_EG_MORE_THAN_49'                       => '',
			'_EG_LESS_THAN_$49'                      => '',
			'_EG_MORE_THAN_$49'                      => '',
		];

		return str_replace( array_keys( $replacements ), array_values( $replacements ), $key );
	}

	/**
	 * Overrides translations if they exist in a normalized form,
	 * which is an alternative key version that is easier and more readable for the user.
	 *
	 * For use in documentation snippets.
	 *
	 * @param array $translations Associative array of translation keys and values.
	 *
	 * @return array Modified array with updated translations.
	 */
	private function standardize_front_translations( array $translations ): array {
		$updated_translations = [];

		foreach ( $translations as $key => $value ) {
			if ( $key === '' ) {
				$updated_translations[ $key ] = $value;
				continue;
			}

			// Skip translations with normalized keys here.
			if ( strpos( $key, '[front]' ) === false ) {
				continue;
			}

			$normalized_key = $this->normalize_translation_key( $key );

			// If the current key also exists in the $translations array in its normalized form,
			// its value takes precedence and will be overwritten.
			if ( isset( $translations[ $normalized_key ] ) ) {
				$updated_translations[ $key ] = $translations[ $normalized_key ];
			} else {
				$updated_translations[ $key ] = $value;
			}
		}

		// Fix if the translation was added as a string instead of an array value.
		foreach ( $updated_translations as $key => $value ) {
			if ( $key !== '' && is_string( $value ) ) {
				$updated_translations[ $key ] = [ $value ];
			}
		}

		return $updated_translations;
	}
}
