<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\TestCase;

/**
 * GR-SEC-004: No external CDN/remote assets in app templates/pages/styles.
 */
final class SecurityRemoteAssetContractTest extends TestCase
{
    use ProjectFileAssertionSupport;

    public function testNoRemoteScriptOrStylesheetTagsInAppTemplatesAndPages(): void
    {
        $files = array_merge(
            $this->collectFilesByExtension('pages', ['php', 'phtml']),
            $this->collectFilesByExtension('templates', ['php', 'phtml']),
            $this->collectModuleFilesByExtension('pages', ['php', 'phtml']),
            $this->collectModuleFilesByExtension('templates', ['php', 'phtml'])
        );

        $pattern = '/<(?:script|link)\b[^>]*\b(?:src|href)\s*=\s*["\']\s*(?:https?:)?\/\/(?!localhost|127\.0\.0\.1)/i';
        $violations = $this->findPatternViolations($files, $pattern);

        self::assertSame(
            [],
            $violations,
            "Remote script/link asset references found (GR-SEC-004):\n" . implode("\n", $violations)
        );
    }

    public function testNoRemoteCssImportsOrFontUrls(): void
    {
        $files = array_merge(
            $this->collectFilesByExtension('web/css', ['css']),
            $this->collectModuleFilesByExtension('web/css', ['css'])
        );

        $importPattern = '/@import\s+(?:url\()?["\']?\s*(?:https?:)?\/\/(?!localhost|127\.0\.0\.1)/i';
        $fontPattern = '/url\(\s*["\']?\s*(?:https?:)?\/\/(?!localhost|127\.0\.0\.1).*?\.(?:woff2?|woff|ttf|otf|eot)\b/i';

        $violations = array_merge(
            $this->findPatternViolations($files, $importPattern),
            $this->findPatternViolations($files, $fontPattern)
        );
        $violations = array_values(array_unique($violations));
        sort($violations);

        self::assertSame(
            [],
            $violations,
            "Remote CSS imports/font URLs found (GR-SEC-004):\n" . implode("\n", $violations)
        );
    }

    /**
     * @param list<string> $extensions
     * @return list<string>
     */
    private function collectFilesByExtension(string $relativeDir, array $extensions): array
    {
        $root = $this->projectRootPath();
        $dir = $root . '/' . $relativeDir;
        if (!is_dir($dir)) {
            return [];
        }

        $result = [];
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS)
        );

        foreach ($iterator as $file) {
            if (!$file->isFile()) {
                continue;
            }
            if (!in_array(strtolower($file->getExtension()), $extensions, true)) {
                continue;
            }

            $result[] = str_replace($root . '/', '', $file->getPathname());
        }

        sort($result);
        return $result;
    }

    /**
     * @param list<string> $extensions
     * @return list<string>
     */
    private function collectModuleFilesByExtension(string $moduleSubDir, array $extensions): array
    {
        $root = $this->projectRootPath();
        $modulesDir = $root . '/modules';
        if (!is_dir($modulesDir)) {
            return [];
        }

        $result = [];
        $entries = scandir($modulesDir) ?: [];
        foreach ($entries as $entry) {
            if ($entry === '.' || $entry === '..') {
                continue;
            }

            $dir = $modulesDir . '/' . $entry . '/' . $moduleSubDir;
            if (!is_dir($dir)) {
                continue;
            }

            $result = array_merge($result, $this->collectFilesByExtension('modules/' . $entry . '/' . $moduleSubDir, $extensions));
        }

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

    /**
     * @param list<string> $files
     * @return list<string>
     */
    private function findPatternViolations(array $files, string $pattern): array
    {
        $violations = [];
        foreach ($files as $relativePath) {
            $content = $this->readProjectFile($relativePath);
            if (preg_match($pattern, $content)) {
                $violations[] = $relativePath;
            }
        }

        sort($violations);
        return $violations;
    }
}
