<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

/**
 * GR-UI-I18N (module extension): Every module with an i18n_path must ship
 * matching default_de.json and default_en.json with identical keys and
 * non-empty values.
 */
class ModuleI18nContractTest extends TestCase
{
    use ProjectFileAssertionSupport;

    /**
     * @return list<array{string, string}>
     */
    public static function moduleProvider(): array
    {
        $root = realpath(__DIR__ . '/../..');
        if ($root === false) {
            return [];
        }

        $modules = [];
        foreach (glob($root . '/modules/*/module.php') as $manifestFile) {
            $raw = (static function (string $path): mixed {
                return require $path;
            })($manifestFile);
            if (!is_array($raw)) {
                continue;
            }
            $i18nPath = $raw['i18n_path'] ?? null;
            if (!is_string($i18nPath) || trim($i18nPath) === '') {
                continue;
            }
            $moduleDir = dirname($manifestFile);
            $moduleId = basename($moduleDir);
            $resolvedPath = rtrim($moduleDir, '/') . '/' . ltrim(trim($i18nPath), '/');
            $modules[$moduleId] = [$moduleId, $resolvedPath];
        }

        return array_values($modules);
    }

    #[DataProvider('moduleProvider')]
    public function testModuleI18nFilesExistAndAreValidJson(string $moduleId, string $i18nPath): void
    {
        $this->assertDirectoryExists($i18nPath, "Module '{$moduleId}' declares i18n_path but directory does not exist.");

        $deFile = $i18nPath . '/default_de.json';
        $enFile = $i18nPath . '/default_en.json';

        $this->assertFileExists($deFile, "Module '{$moduleId}' is missing default_de.json");
        $this->assertFileExists($enFile, "Module '{$moduleId}' is missing default_en.json");

        $deRaw = file_get_contents($deFile);
        $enRaw = file_get_contents($enFile);
        $this->assertNotFalse($deRaw);
        $this->assertNotFalse($enRaw);

        $this->assertIsArray(json_decode($deRaw, true), "Module '{$moduleId}' default_de.json is not valid JSON");
        $this->assertIsArray(json_decode($enRaw, true), "Module '{$moduleId}' default_en.json is not valid JSON");
    }

    #[DataProvider('moduleProvider')]
    public function testModuleI18nKeyParity(string $moduleId, string $i18nPath): void
    {
        $de = $this->loadModuleKeys($i18nPath . '/default_de.json');
        $en = $this->loadModuleKeys($i18nPath . '/default_en.json');

        $missingInEn = array_values(array_diff($de, $en));
        $this->assertSame(
            [],
            $missingInEn,
            "Module '{$moduleId}' default_de.json keys missing from default_en.json:\n" . implode("\n", $missingInEn)
        );

        $missingInDe = array_values(array_diff($en, $de));
        $this->assertSame(
            [],
            $missingInDe,
            "Module '{$moduleId}' default_en.json keys missing from default_de.json:\n" . implode("\n", $missingInDe)
        );
    }

    #[DataProvider('moduleProvider')]
    public function testModuleI18nValuesAreNonEmpty(string $moduleId, string $i18nPath): void
    {
        foreach (['default_de.json', 'default_en.json'] as $file) {
            $path = $i18nPath . '/' . $file;
            if (!is_file($path)) {
                continue;
            }
            $data = json_decode(file_get_contents($path), true);
            $this->assertIsArray($data);

            $empty = array_keys(array_filter($data, static fn ($v): bool => trim((string) $v) === ''));
            $this->assertSame(
                [],
                $empty,
                "Module '{$moduleId}' {$file} has empty values:\n" . implode("\n", $empty)
            );
        }
    }

    /**
     * @return list<string>
     */
    private function loadModuleKeys(string $path): array
    {
        if (!is_file($path)) {
            return [];
        }
        $data = json_decode(file_get_contents($path), true);
        if (!is_array($data)) {
            return [];
        }

        $keys = array_keys($data);
        sort($keys, SORT_STRING);

        return $keys;
    }
}
