<?php

declare(strict_types=1);

use App\Support\OperationRunLinks;
use Tests\Support\OpsUx\SourceFileScanner;

/**
 * @return array<string, string>
 */
function operationRunLinkContractIncludePaths(): array
{
    $root = SourceFileScanner::projectRoot();

    return [
        'environment_recent_operations_summary' => $root.'/app/Filament/Widgets/ManagedEnvironment/RecentOperationsSummary.php',
        'inventory_coverage' => $root.'/app/Filament/Pages/InventoryCoverage.php',
        'inventory_item_resource' => $root.'/app/Filament/Resources/InventoryItemResource.php',
        'review_pack_resource' => $root.'/app/Filament/Resources/ReviewPackResource.php',
        'tenantless_operation_run_viewer' => $root.'/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php',
        'related_navigation_resolver' => $root.'/app/Support/Navigation/RelatedNavigationResolver.php',
        'system_directory_tenant' => $root.'/app/Filament/System/Pages/Directory/ViewTenant.php',
        'system_directory_workspace' => $root.'/app/Filament/System/Pages/Directory/ViewWorkspace.php',
        'system_ops_runs' => $root.'/app/Filament/System/Pages/Ops/Runs.php',
        'system_ops_view_run' => $root.'/app/Filament/System/Pages/Ops/ViewRun.php',
        'admin_panel_provider' => $root.'/app/Providers/Filament/AdminPanelProvider.php',
        'ensure_environment_context_selected' => $root.'/app/Support/Middleware/EnsureEnvironmentContextSelected.php',
        'clear_environment_context_controller' => $root.'/app/Http/Controllers/ClearEnvironmentContextController.php',
        'operation_run_url_delegate' => $root.'/app/Support/OpsUx/OperationRunUrl.php',
    ];
}

/**
 * @return array<string, string>
 */
function operationRunLinkContractAllowlist(): array
{
    return [];
}

/**
 * @param  array<string, string>  $paths
 * @param  array<string, string>  $allowlist
 * @return list<array{file: string, line: int, snippet: string, expectedHelper: string, reason: string}>
 */
function operationRunLinkContractViolations(array $paths, array $allowlist = []): array
{
    $patterns = [
        [
            'pattern' => '/route\(\s*[\'"]admin\.operations\.index[\'"]/',
            'expectedHelper' => 'OperationRunLinks::index(...)',
            'reason' => 'Raw admin operations collection route assembly bypasses the canonical admin link helper.',
        ],
        [
            'pattern' => '/route\(\s*[\'"]admin\.operations\.view[\'"]/',
            'expectedHelper' => 'OperationRunLinks::view(...) or OperationRunLinks::tenantlessView(...)',
            'reason' => 'Raw admin operation detail route assembly bypasses the canonical admin link helper.',
        ],
        [
            'pattern' => '/[\'"]\/system\/ops\/runs(?:\/[^\'"]*)?[\'"]/',
            'expectedHelper' => 'SystemOperationRunLinks::index() or SystemOperationRunLinks::view(...)',
            'reason' => 'Direct system operations path assembly bypasses the canonical system link helper.',
        ],
        [
            'pattern' => '/\b(?:Runs|ViewRun)::getUrl\(/',
            'expectedHelper' => 'SystemOperationRunLinks::index() or SystemOperationRunLinks::view(...)',
            'reason' => 'Direct system operations page URL generation belongs behind the canonical system link helper.',
        ],
    ];

    $violations = [];

    foreach ($paths as $path) {
        if (array_key_exists($path, $allowlist)) {
            continue;
        }

        $source = SourceFileScanner::read($path);
        $lines = preg_split('/\R/', $source) ?: [];

        foreach ($lines as $index => $line) {
            foreach ($patterns as $pattern) {
                if (preg_match($pattern['pattern'], $line) !== 1) {
                    continue;
                }

                $violations[] = [
                    'file' => SourceFileScanner::relativePath($path),
                    'line' => $index + 1,
                    'snippet' => SourceFileScanner::snippet($source, $index + 1),
                    'expectedHelper' => $pattern['expectedHelper'],
                    'reason' => $pattern['reason'],
                ];
            }
        }
    }

    return $violations;
}

it('keeps covered operation run link producers on canonical helper families', function (): void {
    $paths = operationRunLinkContractIncludePaths();
    $allowlist = operationRunLinkContractAllowlist();

    $violations = operationRunLinkContractViolations($paths, $allowlist);

    expect($violations)->toBeEmpty();
})->group('surface-guard');

it('keeps the operation run link exception boundary explicit and infrastructure-owned', function (): void {
    $allowlist = operationRunLinkContractAllowlist();

    expect($allowlist)->toBeEmpty();
})->group('surface-guard');

it('reports actionable file and snippet output for a representative raw bypass', function (): void {
    $probePath = storage_path('framework/testing/OperationRunLinkContractProbe.php');

    if (! is_dir(dirname($probePath))) {
        mkdir(dirname($probePath), 0777, true);
    }

    file_put_contents($probePath, <<<'PHP'
<?php

return route('admin.operations.view', ['run' => 123]);
PHP);

    try {
        $violations = operationRunLinkContractViolations([
            'probe' => $probePath,
        ]);
    } finally {
        @unlink($probePath);
    }

    expect($violations)->toHaveCount(1)
        ->and($violations[0]['file'])->toContain('OperationRunLinkContractProbe.php')
        ->and($violations[0]['line'])->toBe(3)
        ->and($violations[0]['snippet'])->toContain("route('admin.operations.view'")
        ->and($violations[0]['expectedHelper'])->toContain('OperationRunLinks::view')
        ->and($violations[0]['reason'])->toContain('bypasses the canonical admin link helper');
})->group('surface-guard');

it('canonicalizes operation type query parameters for operation collection links', function (): void {
    [$workspace] = localizationWorkspaceMember();

    $url = OperationRunLinks::index(operationType: 'inventory_sync');

    expect($url)->toContain('inventory.sync')
        ->toContain('/admin/workspaces/'.$workspace->getRouteKey().'/operations')
        ->not->toContain('inventory_sync');
})->group('surface-guard');
