<?php
/**
 * I18n - Internationalization library
 * Language detection order: user preference > browser language > English
 */

class I18n
{
    private static ?I18n $instance = null;
    private string $language = DEFAULT_LANGUAGE;
    private array  $translations = [];

    private function __construct() {}

    public static function getInstance(): I18n
    {
        if (self::$instance === null) {
            self::$instance = new I18n();
        }
        return self::$instance;
    }

    /**
     * Initialize language detection.
     * Priority: explicit parameter > session/user preference > browser > default
     */
    public function init(?string $userLang = null): void
    {
        $lang = $this->resolveLanguage($userLang);
        $this->setLanguage($lang);
    }

    public function setLanguage(string $lang): void
    {
        if (!in_array($lang, LANGUAGES)) {
            $lang = DEFAULT_LANGUAGE;
        }
        $this->language = $lang;
        $this->load($lang);
    }

    public function getLanguage(): string
    {
        return $this->language;
    }

    /**
     * Translate a key. Supports {placeholder} substitution.
     */
    public function t(string $key, array $params = []): string
    {
        $text = $this->translations[$key] ?? $key;
        foreach ($params as $placeholder => $value) {
            $text = str_replace('{' . $placeholder . '}', $value, $text);
        }
        return $text;
    }

    // -------------------------------------------------------------------------

    private function resolveLanguage(?string $userLang): string
    {
        // 1. Explicit user preference (stored in DB/session)
        if ($userLang && in_array($userLang, LANGUAGES)) {
            return $userLang;
        }

        // 2. Session stored language
        if (!empty($_SESSION['language']) && in_array($_SESSION['language'], LANGUAGES)) {
            return $_SESSION['language'];
        }

        // 3. Browser Accept-Language header
        $browserLang = $this->detectBrowserLanguage();
        if ($browserLang) {
            return $browserLang;
        }

        // 4. Fallback to English
        return DEFAULT_LANGUAGE;
    }

    private function detectBrowserLanguage(): ?string
    {
        if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            return null;
        }

        // Parse quality-weighted language tags
        $accepted = [];
        $parts = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
        foreach ($parts as $part) {
            $part = trim($part);
            if (preg_match('/^([a-z]{2,3})(?:[-_][A-Z]{2})?(?:;q=([0-9.]+))?$/i', $part, $m)) {
                $code = strtolower(substr($m[1], 0, 2));
                $q    = isset($m[2]) ? (float)$m[2] : 1.0;
                $accepted[$code] = max($accepted[$code] ?? 0, $q);
            }
        }
        arsort($accepted);

        foreach (array_keys($accepted) as $code) {
            if (in_array($code, LANGUAGES)) {
                return $code;
            }
        }
        return null;
    }

    private function load(string $lang): void
    {
        $file = I18N_PATH . '/' . $lang . '.php';
        if (file_exists($file)) {
            $this->translations = require $file;
        } else {
            $fallback = I18N_PATH . '/en.php';
            if (file_exists($fallback)) {
                $this->translations = require $fallback;
            }
        }

        // Merge per-module translations (modules/<name>/i18n/{lang}.php)
        foreach (glob(WEBROOT_PATH . '/modules/*/i18n/' . $lang . '.php') ?: [] as $modFile) {
            $modKeys = require $modFile;
            if (is_array($modKeys)) {
                $this->translations = array_merge($this->translations, $modKeys);
            }
        }
    }
}

// Global shortcut
function t(string $key, array $params = []): string
{
    return I18n::getInstance()->t($key, $params);
}

/**
 * Encode une valeur PHP pour une injection SÛRE dans un bloc <script> inline.
 * Ajoute les drapeaux JSON_HEX_* pour qu'aucune valeur (ex. sourcée DB/AD) ne
 * puisse casser le contexte via `</script>`, `'`, `"`, `&` ou les séparateurs
 * de ligne U+2028/U+2029. À utiliser partout où l'on fait `<?= json_encode(...) ?>`
 * dans du HTML.
 */
function js_json(mixed $value): string
{
    return json_encode(
        $value,
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
}
