<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\TestCase;

final class FrontendJsContractsTest extends TestCase
{
    use ProjectFileAssertionSupport;

    public function testNoWindowAlertCallsInFrontendJsScope(): void
    {
        $violations = $this->findPatternMatchesInFiles('web/js', '/window\.alert\s*\(/', ['js']);
        foreach ($this->moduleJsDirectories() as $moduleJsDir) {
            $violations = array_merge($violations, $this->findPatternMatchesInFiles($moduleJsDir, '/window\.alert\s*\(/', ['js']));
        }

        $violations = array_values(array_unique($violations));
        sort($violations);

        $this->assertSame([], $violations, "window.alert usage found in frontend JS scope:\n" . implode("\n", $violations));
    }

    public function testFrontendJsDoesNotUseDomReadyAutoInitPatterns(): void
    {
        $violations = $this->findPatternMatchesInFiles('web/js', '/DOMContentLoaded|document\.readyState/', ['js']);
        foreach ($this->moduleJsDirectories() as $moduleJsDir) {
            $violations = array_merge($violations, $this->findPatternMatchesInFiles($moduleJsDir, '/DOMContentLoaded|document\.readyState/', ['js']));
        }

        $violations = array_values(array_unique($violations));
        sort($violations);

        $this->assertSame([], $violations, "DOM-ready auto-init pattern found in frontend JS scope:\n" . implode("\n", $violations));
    }

    public function testHelpdeskRuntimeComponentsDoNotAutoInitialize(): void
    {
        foreach ($this->helpdeskRuntimeComponentFiles() as $file) {
            $content = $this->readProjectFile($file);
            $this->assertStringNotContainsString('DOMContentLoaded', $content, $file);
            $this->assertStringNotContainsString("if (document.readyState === 'loading')", $content, $file);
            $this->assertStringNotContainsString('const container = document.querySelector', $content, $file);
        }
    }

    public function testHelpdeskRuntimeComponentsExposeDestroyLifecycle(): void
    {
        foreach ($this->helpdeskRuntimeComponentFiles() as $file) {
            $content = $this->readProjectFile($file);
            $this->assertStringContainsString('destroy:', $content, $file);
        }
    }

    public function testFrontendJsUsesCentralHttpLayer(): void
    {
        $violations = $this->findDirectFetchCallsInFrontendScope();
        $this->assertSame([], $violations, "Direct fetch(...) found outside approved HTTP infrastructure:\n" . implode("\n", $violations));
    }

    public function testModuleImportPolicyForCoreAndSiblingModules(): void
    {
        $root = $this->projectRootPath();
        $violations = [];

        foreach ($this->moduleJsDirectories() as $moduleJsDir) {
            $base = $root . '/' . $moduleJsDir;
            $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($base, \FilesystemIterator::SKIP_DOTS));

            /** @var \SplFileInfo $file */
            foreach ($iterator as $file) {
                if (!$file->isFile() || $file->getExtension() !== 'js') {
                    continue;
                }

                $content = file_get_contents($file->getPathname());
                $this->assertNotFalse($content, 'Could not read file: ' . $file->getPathname());
                $relativePath = str_replace($root . '/', '', $file->getPathname());

                preg_match_all('/^\s*import\s+(?:[^\"\']+\s+from\s+)?[\"\']([^\"\']+)[\"\']/m', $content, $matches);
                foreach ($matches[1] as $importPathRaw) {
                    $importPath = (string) $importPathRaw;

                    if (str_starts_with($importPath, '/js/')) {
                        continue;
                    }

                    if (str_starts_with($importPath, './') || str_starts_with($importPath, '../')) {
                        if (preg_match('#(?:^|/)js/(core|components|pages)/#', $importPath) === 1) {
                            $violations[] = $relativePath . ' => relative core import: ' . $importPath;
                        }
                        continue;
                    }

                    $violations[] = $relativePath . ' => invalid import path: ' . $importPath;
                }
            }
        }

        $violations = array_values(array_unique($violations));
        sort($violations);

        $this->assertSame([], $violations, "Module import policy violations:\n" . implode("\n", $violations));
    }

    public function testLookupFieldComponentExportsRuntimeInitializerWithoutAutoInit(): void
    {
        $content = $this->readProjectFile('web/js/components/app-lookup-field.js');

        $this->assertStringContainsString('export function initLookupFields', $content);
        $this->assertStringContainsString('destroy:', $content);
        $this->assertStringNotContainsString('DOMContentLoaded', $content);
    }

    /**
     * @return list<string>
     */
    private function helpdeskRuntimeComponentFiles(): array
    {
        return [
            'modules/helpdesk/web/js/helpdesk-detail.js',
            'modules/helpdesk/web/js/helpdesk-domain-detail.js',
            'modules/helpdesk/web/js/helpdesk-ticket.js',
            'modules/helpdesk/web/js/helpdesk-team.js',
            'modules/helpdesk/web/js/helpdesk-risk-radar.js',
            'modules/helpdesk/web/js/helpdesk-settings.js',
            'modules/helpdesk/web/js/handover-domain-select.js',
            'modules/helpdesk/web/js/handover-schema-editor.js',
        ];
    }

    /**
     * @return list<string>
     */
    private function moduleJsDirectories(): array
    {
        $root = $this->projectRootPath();
        $modulesRoot = $root . '/modules';
        $this->assertDirectoryExists($modulesRoot, 'Modules root not found.');

        $dirs = [];
        $entries = scandir($modulesRoot);
        $this->assertNotFalse($entries, 'Could not read modules directory.');
        foreach ($entries as $entry) {
            if ($entry === '.' || $entry === '..') {
                continue;
            }
            $jsPath = $modulesRoot . '/' . $entry . '/web/js';
            if (is_dir($jsPath)) {
                $dirs[] = 'modules/' . $entry . '/web/js';
            }
        }

        sort($dirs);
        return $dirs;
    }

    /**
     * @return list<string>
     */
    private function findDirectFetchCallsInFrontendScope(): array
    {
        $root = $this->projectRootPath();
        $allowed = [
            'web/js/core/app-http.js',
            'web/js/core/app-telemetry.js',
        ];
        $scanRoots = array_merge(['web/js'], $this->moduleJsDirectories());
        $violations = [];

        foreach ($scanRoots as $scanRoot) {
            $base = $root . '/' . $scanRoot;
            $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($base, \FilesystemIterator::SKIP_DOTS));
            /** @var \SplFileInfo $file */
            foreach ($iterator as $file) {
                if (!$file->isFile() || $file->getExtension() !== 'js') {
                    continue;
                }

                $relativePath = str_replace($root . '/', '', $file->getPathname());
                if (in_array($relativePath, $allowed, true)) {
                    continue;
                }

                $content = file_get_contents($file->getPathname());
                $this->assertNotFalse($content, 'Could not read file: ' . $file->getPathname());
                if (preg_match('/\bfetch\s*\(/', $content) === 1) {
                    $violations[] = $relativePath;
                }
            }
        }

        $violations = array_values(array_unique($violations));
        sort($violations);
        return $violations;
    }
}
