<?php

namespace MintyPHP\Tests\I18n;

use PHPUnit\Framework\TestCase;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;

class TranslationKeysTest extends TestCase
{
    public function testAllTranslationKeysExistInDefaultLocales(): void
    {
        $root = realpath(__DIR__ . '/../..');
        $this->assertNotFalse($root, 'Project root not found.');

        // Core scan paths
        $corePaths = [
            $root . '/pages',
            $root . '/templates',
            $root . '/lib',
            $root . '/web/js',
        ];
        $extensions = ['php', 'phtml', 'js'];

        // Collect core keys
        $coreKeys = $this->collectKeysFromPaths($corePaths, $extensions);

        // Load core translations
        $coreTranslations = $this->loadTranslationMap($root . '/i18n', ['default_de.json', 'default_en.json']);

        // Also load all module translations so core keys provided by modules are available
        $moduleTranslations = $this->loadAllModuleTranslations($root . '/modules');
        $mergedTranslations = [];
        foreach ($coreTranslations as $name => $map) {
            $mergedTranslations[$name] = $map;
        }
        foreach ($moduleTranslations as $name => $maps) {
            foreach ($maps as $locale => $map) {
                $mergedTranslations[$locale] = array_merge($mergedTranslations[$locale] ?? [], $map);
            }
        }

        // Validate core keys exist in merged (core + module) translations
        $missing = [];
        foreach ($coreKeys as $key) {
            foreach ($mergedTranslations as $name => $map) {
                if (!array_key_exists($key, $map)) {
                    $missing[$name][] = $key;
                }
            }
        }

        $messages = [];
        foreach ($missing as $name => $keys) {
            $messages[] = $name . ' missing: ' . implode(', ', $keys);
        }
        $this->assertSame([], $missing, implode(' | ', $messages));
    }

    public function testAllModuleTranslationKeysExist(): void
    {
        $root = realpath(__DIR__ . '/../..');
        $this->assertNotFalse($root, 'Project root not found.');

        $modulesDir = $root . '/modules';
        if (!is_dir($modulesDir)) {
            $this->markTestSkipped('No modules directory found.');
        }

        $extensions = ['php', 'phtml', 'js'];

        // Load core translations as fallback
        $coreTranslations = $this->loadTranslationMap($root . '/i18n', ['default_de.json', 'default_en.json']);

        $allMissing = [];

        foreach (glob($modulesDir . '/*/module.php') as $manifestFile) {
            $moduleDir = dirname($manifestFile);
            $moduleId = basename($moduleDir);

            // Scan module directories for keys
            $modulePaths = [
                $moduleDir . '/pages',
                $moduleDir . '/templates',
                $moduleDir . '/lib',
                $moduleDir . '/web',
            ];
            $moduleKeys = $this->collectKeysFromPaths($modulePaths, $extensions);
            if ($moduleKeys === []) {
                continue;
            }

            // Load module translations and merge with core
            $moduleI18nDir = $moduleDir . '/i18n';
            $moduleTranslations = is_dir($moduleI18nDir)
                ? $this->loadTranslationMap($moduleI18nDir, ['default_de.json', 'default_en.json'])
                : [];

            $merged = [];
            foreach ($coreTranslations as $locale => $map) {
                $merged[$locale] = array_merge($map, $moduleTranslations[$locale] ?? []);
            }

            foreach ($moduleKeys as $key) {
                foreach ($merged as $locale => $map) {
                    if (!array_key_exists($key, $map)) {
                        $allMissing["modules/{$moduleId} → {$locale}"][] = $key;
                    }
                }
            }
        }

        $messages = [];
        foreach ($allMissing as $context => $keys) {
            $messages[] = $context . ' missing: ' . implode(', ', $keys);
        }
        $this->assertSame([], $allMissing, implode(' | ', $messages));
    }

    /**
     * @param list<string> $paths
     * @param list<string> $extensions
     * @return list<string>
     */
    private function collectKeysFromPaths(array $paths, array $extensions): array
    {
        $usedKeys = [];
        foreach ($paths as $path) {
            if (!is_dir($path)) {
                continue;
            }
            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
            foreach ($iterator as $file) {
                if (!$file->isFile()) {
                    continue;
                }
                $ext = strtolower((string) $file->getExtension());
                if (!in_array($ext, $extensions, true)) {
                    continue;
                }
                $content = file_get_contents($file->getPathname());
                if ($content === false || $content === '') {
                    continue;
                }
                foreach ($this->extractTranslationKeys($content) as $key) {
                    $usedKeys[$key] = true;
                }
            }
        }

        $keys = array_keys($usedKeys);
        sort($keys);

        return $keys;
    }

    /**
     * @param list<string> $files
     * @return array<string, array<string, string>>
     */
    private function loadTranslationMap(string $dir, array $files): array
    {
        $translations = [];
        foreach ($files as $name) {
            $path = $dir . '/' . $name;
            if (!is_file($path)) {
                continue;
            }
            $raw = file_get_contents($path);
            if ($raw === false) {
                continue;
            }
            $json = json_decode($raw, true);
            if (is_array($json)) {
                $translations[$name] = $json;
            }
        }

        return $translations;
    }

    /**
     * @return array<string, array<string, array<string, string>>>
     */
    private function loadAllModuleTranslations(string $modulesDir): array
    {
        $result = [];
        if (!is_dir($modulesDir)) {
            return $result;
        }

        foreach (glob($modulesDir . '/*/i18n') as $i18nDir) {
            $moduleId = basename(dirname($i18nDir));
            foreach (['default_de.json', 'default_en.json'] as $file) {
                $path = $i18nDir . '/' . $file;
                if (!is_file($path)) {
                    continue;
                }
                $raw = file_get_contents($path);
                if ($raw === false) {
                    continue;
                }
                $json = json_decode($raw, true);
                if (is_array($json)) {
                    $result[$moduleId][$file] = $json;
                }
            }
        }

        return $result;
    }

    /**
     * @return list<string>
     */
    private function extractTranslationKeys(string $content): array
    {
        $keys = [];
        // Match t('...') with escaped quotes inside, and the same for t("...").
        $patterns = [
            "/\\bt\\s*\\(\\s*'((?:\\\\\\\\.|[^'\\\\\\\\])*)'\\s*\\)/",
            '/\\bt\\s*\\(\\s*"((?:\\\\\\\\.|[^"\\\\\\\\])*)"\\s*\\)/',
        ];

        foreach ($patterns as $pattern) {
            if (!preg_match_all($pattern, $content, $matches)) {
                continue;
            }
            foreach ($matches[1] as $match) {
                $keys[] = stripcslashes($match);
            }
        }

        return $keys;
    }
}
