<?php

use Illuminate\Support\Collection;

it('Spec081 prevents legacy tenant credential writes outside approved maintenance surfaces', function (): void {
    $root = base_path();
    $self = realpath(__FILE__);

    $directories = [
        $root.'/app',
    ];

    $excludedPaths = [
        $root.'/vendor',
        $root.'/storage',
        $root.'/specs',
        $root.'/spechistory',
        $root.'/references',
        $root.'/bootstrap/cache',
    ];

    $allowlist = [
        // Model cast declaration only (not a runtime write path).
        'app/Models/ManagedEnvironment.php',
    ];

    $forbiddenPatterns = [
        "/'app_client_id'\\s*=>/",
        "/'app_client_secret'\\s*=>/",
        '/->app_client_id\s*=/',
        '/->app_client_secret\s*=/',
    ];

    /** @var Collection<int, string> $files */
    $files = collect($directories)
        ->filter(fn (string $dir): bool => is_dir($dir))
        ->flatMap(function (string $dir): array {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
            );

            $paths = [];

            foreach ($iterator as $file) {
                if (! $file->isFile()) {
                    continue;
                }

                $path = $file->getPathname();

                if (! str_ends_with($path, '.php')) {
                    continue;
                }

                $paths[] = $path;
            }

            return $paths;
        })
        ->filter(function (string $path) use ($excludedPaths, $self): bool {
            if ($self && realpath($path) === $self) {
                return false;
            }

            foreach ($excludedPaths as $excluded) {
                if (str_starts_with($path, $excluded)) {
                    return false;
                }
            }

            return true;
        })
        ->values();

    $hits = [];

    foreach ($files as $path) {
        $relative = str_replace($root.'/', '', $path);

        if (in_array($relative, $allowlist, true)) {
            continue;
        }

        $contents = file_get_contents($path);

        if (! is_string($contents) || $contents === '') {
            continue;
        }

        foreach ($forbiddenPatterns as $pattern) {
            if (! preg_match($pattern, $contents)) {
                continue;
            }

            $lines = preg_split('/\R/', $contents) ?: [];

            foreach ($lines as $index => $line) {
                if (preg_match($pattern, $line)) {
                    $hits[] = $relative.':'.($index + 1).' -> '.trim($line);
                }
            }
        }
    }

    expect($hits)->toBeEmpty("Legacy tenant credential writes detected:\n".implode("\n", $hits));
});

it('Spec081 blocks new runtime reads of legacy tenant credentials outside the current cutover allowlist', function (): void {
    $root = base_path();
    $self = realpath(__FILE__);

    $directories = [
        $root.'/app',
    ];

    $excludedPaths = [
        $root.'/vendor',
        $root.'/storage',
        $root.'/specs',
        $root.'/spechistory',
        $root.'/references',
        $root.'/bootstrap/cache',
    ];

    $allowlist = [
        // NOTE: Shrink this list while finishing Spec081 service cutovers.
        'app/Models/ManagedEnvironment.php',
        'app/Services/Intune/TenantConfigService.php',
        'app/Services/Intune/ManagedEnvironmentPermissionService.php',
        'app/Console/Commands/ReclassifyEnrollmentConfigurations.php',
        'app/Console/Commands/TenantpilotBackfillMicrosoftDefaultProviderConnections.php',
        'app/Services/Providers/ProviderConnectionBackfillService.php',
    ];

    $forbiddenPatterns = [
        '/->app_client_id\b/',
        '/->app_client_secret\b/',
    ];

    /** @var Collection<int, string> $files */
    $files = collect($directories)
        ->filter(fn (string $dir): bool => is_dir($dir))
        ->flatMap(function (string $dir): array {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
            );

            $paths = [];

            foreach ($iterator as $file) {
                if (! $file->isFile()) {
                    continue;
                }

                $path = $file->getPathname();

                if (! str_ends_with($path, '.php')) {
                    continue;
                }

                $paths[] = $path;
            }

            return $paths;
        })
        ->filter(function (string $path) use ($excludedPaths, $self): bool {
            if ($self && realpath($path) === $self) {
                return false;
            }

            foreach ($excludedPaths as $excluded) {
                if (str_starts_with($path, $excluded)) {
                    return false;
                }
            }

            return true;
        })
        ->values();

    $hits = [];

    foreach ($files as $path) {
        $relative = str_replace($root.'/', '', $path);

        if (in_array($relative, $allowlist, true)) {
            continue;
        }

        $contents = file_get_contents($path);

        if (! is_string($contents) || $contents === '') {
            continue;
        }

        foreach ($forbiddenPatterns as $pattern) {
            if (! preg_match($pattern, $contents)) {
                continue;
            }

            $lines = preg_split('/\R/', $contents) ?: [];

            foreach ($lines as $index => $line) {
                if (preg_match($pattern, $line)) {
                    $hits[] = $relative.':'.($index + 1).' -> '.trim($line);
                }
            }
        }
    }

    expect($hits)->toBeEmpty(
        "Legacy tenant credential reads found outside allowlist (shrink allowlist over time):\n".implode("\n", $hits)
    );
});

it('Spec081 blocks ManagedEnvironment::graphOptions helper usage in cutover runtime services and workers', function (): void {
    $root = base_path();

    $files = [
        'app/Services/Inventory/InventorySyncService.php',
        'app/Services/Graph/ScopeTagResolver.php',
        'app/Services/Intune/PolicySyncService.php',
        'app/Services/Intune/PolicySnapshotService.php',
        'app/Services/Intune/RestoreService.php',
        'app/Services/Intune/RbacOnboardingService.php',
        'app/Services/Intune/RbacHealthService.php',
        'app/Jobs/SyncPoliciesJob.php',
        'app/Jobs/Operations/TenantSyncWorkerJob.php',
        'app/Jobs/Operations/CapturePolicySnapshotWorkerJob.php',
        'app/Jobs/ExecuteRestoreRunJob.php',
    ];

    $hits = [];

    foreach ($files as $relativePath) {
        $absolutePath = $root.'/'.$relativePath;

        if (! is_file($absolutePath)) {
            continue;
        }

        $contents = file_get_contents($absolutePath);

        if (! is_string($contents) || $contents === '') {
            continue;
        }

        if (! preg_match('/\$tenant->graphOptions\(|ManagedEnvironment::graphOptions\(/', $contents)) {
            continue;
        }

        $lines = preg_split('/\R/', $contents) ?: [];

        foreach ($lines as $index => $line) {
            if (preg_match('/\$tenant->graphOptions\(|ManagedEnvironment::graphOptions\(/', $line)) {
                $hits[] = $relativePath.':'.($index + 1).' -> '.trim($line);
            }
        }
    }

    expect($hits)->toBeEmpty("ManagedEnvironment::graphOptions usage detected in cutover runtime paths:\n".implode("\n", $hits));
});

it('Spec081 enforces ProviderGateway as the single runtime entry point for provider credential reads', function (): void {
    $root = base_path();
    $self = realpath(__FILE__);

    $directories = [
        $root.'/app',
    ];

    $excludedPaths = [
        $root.'/vendor',
        $root.'/storage',
        $root.'/specs',
        $root.'/spechistory',
        $root.'/references',
        $root.'/bootstrap/cache',
    ];

    $allowlist = [
        'app/Services/Providers/CredentialManager.php',
        'app/Services/Providers/ProviderGateway.php',
        'app/Services/Providers/ProviderIdentityResolver.php',
    ];

    $forbiddenPattern = '/->getClientCredentials\(/';

    /** @var Collection<int, string> $files */
    $files = collect($directories)
        ->filter(fn (string $dir): bool => is_dir($dir))
        ->flatMap(function (string $dir): array {
            $iterator = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
            );

            $paths = [];

            foreach ($iterator as $file) {
                if (! $file->isFile()) {
                    continue;
                }

                $path = $file->getPathname();

                if (! str_ends_with($path, '.php')) {
                    continue;
                }

                $paths[] = $path;
            }

            return $paths;
        })
        ->filter(function (string $path) use ($excludedPaths, $self): bool {
            if ($self && realpath($path) === $self) {
                return false;
            }

            foreach ($excludedPaths as $excluded) {
                if (str_starts_with($path, $excluded)) {
                    return false;
                }
            }

            return true;
        })
        ->values();

    $hits = [];

    foreach ($files as $path) {
        $relative = str_replace($root.'/', '', $path);

        if (in_array($relative, $allowlist, true)) {
            continue;
        }

        $contents = file_get_contents($path);

        if (! is_string($contents) || $contents === '') {
            continue;
        }

        if (! preg_match($forbiddenPattern, $contents)) {
            continue;
        }

        $lines = preg_split('/\R/', $contents) ?: [];

        foreach ($lines as $index => $line) {
            if (preg_match($forbiddenPattern, $line)) {
                $hits[] = $relative.':'.($index + 1).' -> '.trim($line);
            }
        }
    }

    expect($hits)->toBeEmpty(
        "Direct CredentialManager::getClientCredentials usage detected outside ProviderGateway:\n".implode("\n", $hits)
    );
});
