<?php

namespace MintyPHP\Tests\Architecture;

use PHPUnit\Framework\TestCase;

/**
 * Repository layer must not access HTTP superglobals or emit HTTP responses.
 * Repositories are pure data access — no session, request, or response handling.
 */
class RepositoryLayerIsolationContractTest extends TestCase
{
    use ProjectFileAssertionSupport;

    /** @var array<string, string> */
    private const FORBIDDEN_PATTERNS = [
        '/\$_SESSION\b/' => '$_SESSION',
        '/\$_GET\b/' => '$_GET',
        '/\$_POST\b/' => '$_POST',
        '/\$_COOKIE\b/' => '$_COOKIE',
        '/\$_REQUEST\b/' => '$_REQUEST',
        '/\bheader\s*\(/' => 'header()',
        '/\bhttp_response_code\s*\(/' => 'http_response_code()',
        '/\bsetcookie\s*\(/' => 'setcookie()',
        '/\bsession_start\s*\(/' => 'session_start()',
    ];

    public function testCoreRepositoriesDoNotAccessHttpSuperglobals(): void
    {
        $this->assertRepositoryIsolation('core/Repository');
    }

    public function testModuleRepositoriesDoNotAccessHttpSuperglobals(): void
    {
        $root = $this->projectRootPath();
        $modulesDir = $root . '/modules';
        if (!is_dir($modulesDir)) {
            self::markTestSkipped('modules/ directory missing.');
        }

        foreach (scandir($modulesDir) ?: [] as $entry) {
            if ($entry === '.' || $entry === '..' || !is_dir($modulesDir . '/' . $entry)) {
                continue;
            }
            $libRel = 'modules/' . $entry . '/lib';
            if (!is_dir($root . '/' . $libRel)) {
                continue;
            }

            // Scan only Repository directories within module lib/
            $repoDir = $libRel . '/Repository';
            if (is_dir($root . '/' . $repoDir)) {
                $this->assertRepositoryIsolation($repoDir);
            }

            // Also scan files named *Repository.php anywhere in module lib/
            $this->assertRepositoryFilesIsolated($libRel);
        }
    }

    private function assertRepositoryIsolation(string $relativeDirectory): void
    {
        $violations = [];

        foreach (self::FORBIDDEN_PATTERNS as $pattern => $label) {
            foreach ($this->findPatternMatchesInPhpFiles($relativeDirectory, $pattern) as $file) {
                $violations[] = "{$file} uses {$label}";
            }
        }

        self::assertSame(
            [],
            $violations,
            "Repository layer must not access HTTP state. Violations:\n" . implode("\n", $violations)
        );
    }

    private function assertRepositoryFilesIsolated(string $relativeDirectory): void
    {
        $root = $this->projectRootPath();
        $basePath = $root . '/' . $relativeDirectory;
        $violations = [];

        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($basePath, \FilesystemIterator::SKIP_DOTS),
            \RecursiveIteratorIterator::LEAVES_ONLY
        );

        foreach ($iterator as $file) {
            /** @var \SplFileInfo $file */
            if ($file->getExtension() !== 'php') {
                continue;
            }
            if (!str_ends_with($file->getFilename(), 'Repository.php')) {
                continue;
            }
            if (str_ends_with($file->getFilename(), 'RepositoryInterface.php')) {
                continue;
            }

            $content = file_get_contents($file->getPathname());
            if ($content === false) {
                continue;
            }

            $relativePath = str_replace($root . '/', '', $file->getPathname());
            foreach (self::FORBIDDEN_PATTERNS as $pattern => $label) {
                if (preg_match($pattern, $content)) {
                    $violations[] = "{$relativePath} uses {$label}";
                }
            }
        }

        self::assertSame(
            [],
            $violations,
            "Repository files must not access HTTP state. Violations:\n" . implode("\n", $violations)
        );
    }
}
