<?php

namespace App\Core;

/**
 * ITSWEBER CMS – Translator
 *
 * Central translation service for the CMS.
 * Loads language files from app/Lang/{locale}/ and provides
 * key-based translation with dot-notation and placeholder support.
 *
 * Fallback chain: requested locale → fallback locale ('de').
 *
 * © Benjamin Weber / ITSWEBER – www.itsweber.de
 */
class Translator
{
    private string $locale;
    private string $fallbackLocale;
    private string $langPath;
    private array $loaded = [];
    private array $cache = [];

    /** @var string[] Supported locales */
    private const SUPPORTED_LOCALES = ['de', 'en'];

    public function __construct(string $langPath, string $locale = 'de', string $fallbackLocale = 'de')
    {
        $this->langPath = rtrim($langPath, '/');
        $this->fallbackLocale = $fallbackLocale;
        $this->setLocale($locale);
    }

    /**
     * Translate a key.
     *
     * Keys use dot-notation: "file.section.key"
     * The first segment is the filename, the rest is the array path.
     *
     * Example: __('admin.nav.dashboard') loads app/Lang/{locale}/admin.php
     * and resolves ['nav']['dashboard'].
     *
     * @param string $key    Dot-notation translation key
     * @param array  $params Placeholder replacements [':name' => 'value']
     * @return string Translated string, or the key itself if not found
     */
    public function get(string $key, array $params = []): string
    {
        // Try cache first
        $cacheKey = $this->locale . '.' . $key;
        if (isset($this->cache[$cacheKey])) {
            return $this->replacePlaceholders($this->cache[$cacheKey], $params);
        }

        // Parse key: first segment = file, rest = path
        $segments = explode('.', $key);
        if (count($segments) < 2) {
            return $this->replacePlaceholders($key, $params);
        }

        $file = array_shift($segments);
        $path = $segments;

        // Try current locale
        $value = $this->resolve($file, $path, $this->locale);

        // Fallback to default locale
        if ($value === null && $this->locale !== $this->fallbackLocale) {
            $value = $this->resolve($file, $path, $this->fallbackLocale);
        }

        // If still not found, return the key itself (makes missing translations visible)
        if ($value === null) {
            return $this->replacePlaceholders($key, $params);
        }

        $this->cache[$cacheKey] = $value;
        return $this->replacePlaceholders($value, $params);
    }

    /**
     * Set the active locale.
     */
    public function setLocale(string $locale): void
    {
        $this->locale = in_array($locale, self::SUPPORTED_LOCALES, true) ? $locale : $this->fallbackLocale;
    }

    /**
     * Get the active locale.
     */
    public function getLocale(): string
    {
        return $this->locale;
    }

    /**
     * Get the fallback locale.
     */
    public function getFallbackLocale(): string
    {
        return $this->fallbackLocale;
    }

    /**
     * Get all supported locales.
     *
     * @return string[]
     */
    public static function supportedLocales(): array
    {
        return self::SUPPORTED_LOCALES;
    }

    /**
     * Check if a locale is supported.
     */
    public static function isSupported(string $locale): bool
    {
        return in_array($locale, self::SUPPORTED_LOCALES, true);
    }

    /**
     * Clear the translation cache (useful after locale change).
     */
    public function clearCache(): void
    {
        $this->cache = [];
    }

    // ------------------------------------------------------------------
    // Internal
    // ------------------------------------------------------------------

    /**
     * Resolve a translation value from loaded language data.
     */
    private function resolve(string $file, array $path, string $locale): ?string
    {
        $this->loadFile($file, $locale);

        $data = $this->loaded[$locale][$file] ?? [];

        foreach ($path as $segment) {
            if (!is_array($data) || !array_key_exists($segment, $data)) {
                return null;
            }
            $data = $data[$segment];
        }

        return is_string($data) ? $data : null;
    }

    /**
     * Load a language file if not already loaded.
     */
    private function loadFile(string $file, string $locale): void
    {
        if (isset($this->loaded[$locale][$file])) {
            return;
        }

        $filePath = $this->langPath . '/' . $locale . '/' . $file . '.php';

        if (is_file($filePath)) {
            $data = include $filePath;
            $this->loaded[$locale][$file] = is_array($data) ? $data : [];
        } else {
            $this->loaded[$locale][$file] = [];
        }
    }

    /**
     * Replace :placeholder tokens in a translated string.
     */
    private function replacePlaceholders(string $text, array $params): string
    {
        if (empty($params)) {
            return $text;
        }

        foreach ($params as $placeholder => $value) {
            // Support both ':name' and 'name' as keys
            $token = str_starts_with($placeholder, ':') ? $placeholder : ':' . $placeholder;
            $text = str_replace($token, (string) $value, $text);
        }

        return $text;
    }
}
