<?php

declare(strict_types=1);

use App\Support\Ui\ActionSurface\ActionSurfaceDiscoveredComponent;
use App\Support\Ui\ActionSurface\ActionSurfaceExemptions;
use App\Support\Ui\ActionSurface\ActionSurfaceValidator;

/**
 * @return array<int, string>
 */
function spec195DiscoveredClasses(): array
{
    return array_map(
        static fn (ActionSurfaceDiscoveredComponent $component): string => $component->className,
        ActionSurfaceValidator::withBaselineExemptions()->discoveredComponents(),
    );
}

/**
 * @param  array<int, \App\Support\Ui\ActionSurface\ActionSurfaceValidationIssue>  $issues
 */
function spec195FormattedIssues(array $issues): string
{
    return implode("\n", array_map(
        static fn ($issue): string => $issue->format(),
        $issues,
    ));
}

it('keeps every spec 195 residual surface classified exactly once with structured evidence', function (): void {
    $inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();

    expect(array_keys($inventory))->toEqualCanonicalizing([
        \App\Filament\System\Pages\Dashboard::class,
        \App\Filament\System\Pages\Ops\ViewRun::class,
        \App\Filament\System\Pages\Ops\Runbooks::class,
        \App\Filament\System\Pages\Ops\Controls::class,
        \App\Filament\System\Pages\RepairWorkspaceOwners::class,
        \App\Filament\System\Pages\Directory\ViewTenant::class,
        \App\Filament\System\Pages\Directory\ViewWorkspace::class,
        \App\Filament\Pages\BreakGlassRecovery::class,
        \App\Filament\Pages\ChooseWorkspace::class,
        \App\Filament\Pages\ChooseEnvironment::class,
        \App\Filament\Pages\Tenancy\RegisterTenant::class,
        \App\Filament\Pages\Workspaces\ManagedEnvironmentOnboardingWizard::class,
        \App\Filament\Pages\Workspaces\ManagedEnvironmentsLanding::class,
        \App\Filament\Pages\EnvironmentDashboard::class,
    ]);

    $surfaceKeys = collect($inventory)->pluck('surfaceKey')->all();

    expect($surfaceKeys)->toHaveCount(count(array_unique($surfaceKeys)));

    foreach ($inventory as $className => $surface) {
        expect($surface['closureDecision'] ?? null)
            ->not->toBeNull("{$className} must keep a closure decision.")
            ->and(trim((string) ($surface['surfaceName'] ?? '')))
            ->not->toBe('', "{$className} must keep a human-readable surfaceName.")
            ->and($surface['evidence'] ?? [])
            ->not->toBeEmpty("{$className} must keep structured evidence.");
    }
});

it('keeps the residual system tail explicitly classified instead of silently baseline-exempted', function (): void {
    $inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
    $baselineExemptions = ActionSurfaceExemptions::baseline()->all();

    expect($inventory[\App\Filament\System\Pages\Ops\ViewRun::class]['closureDecision'] ?? null)->toBe('separately_governed')
        ->and($inventory[\App\Filament\System\Pages\Ops\Runbooks::class]['closureDecision'] ?? null)->toBe('separately_governed')
        ->and($inventory[\App\Filament\System\Pages\Ops\Controls::class]['closureDecision'] ?? null)->toBe('separately_governed')
        ->and($inventory[\App\Filament\System\Pages\RepairWorkspaceOwners::class]['closureDecision'] ?? null)->toBe('separately_governed')
        ->and($inventory[\App\Filament\System\Pages\Directory\ViewTenant::class]['closureDecision'] ?? null)->toBe('harmless_special_case')
        ->and($inventory[\App\Filament\System\Pages\Directory\ViewWorkspace::class]['closureDecision'] ?? null)->toBe('harmless_special_case')
        ->and($inventory[\App\Filament\System\Pages\Dashboard::class]['closureDecision'] ?? null)->toBe('separately_governed');

    foreach ([
        \App\Filament\System\Pages\Dashboard::class,
        \App\Filament\System\Pages\Ops\ViewRun::class,
        \App\Filament\System\Pages\Ops\Runbooks::class,
        \App\Filament\System\Pages\Ops\Controls::class,
        \App\Filament\System\Pages\RepairWorkspaceOwners::class,
        \App\Filament\System\Pages\Directory\ViewTenant::class,
        \App\Filament\System\Pages\Directory\ViewWorkspace::class,
    ] as $className) {
        expect(array_key_exists($className, $baselineExemptions))
            ->toBeFalse("{$className} must not rely on baseline() for Spec 195 closure.");
    }
});

it('retires break glass recovery from live baseline handling', function (): void {
    $surface = ActionSurfaceExemptions::spec195ResidualSurface(\App\Filament\Pages\BreakGlassRecovery::class);

    expect($surface)->not->toBeNull()
        ->and($surface['closureDecision'] ?? null)->toBe('retired_no_longer_relevant')
        ->and($surface['reasonCategory'] ?? null)->toBe('disabled_or_actionless_surface')
        ->and(ActionSurfaceExemptions::baseline()->hasClass(\App\Filament\Pages\BreakGlassRecovery::class))->toBeFalse();
});

it('fails when a residual candidate is missing a closure decision entry', function (): void {
    $inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
    unset($inventory[\App\Filament\System\Pages\Dashboard::class]);

    $issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
        inventory: $inventory,
        discoveredClasses: spec195DiscoveredClasses(),
        baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
        residualCandidateClasses: [\App\Filament\System\Pages\Dashboard::class],
    );

    expect(spec195FormattedIssues($issues))
        ->toContain('Residual action surface is missing a Spec 195 closure entry')
        ->toContain(\App\Filament\System\Pages\Dashboard::class);
});

it('fails when a discovered residual exemption loses its reason category', function (): void {
    $inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
    $inventory[\App\Filament\Pages\ChooseEnvironment::class]['reasonCategory'] = null;

    $issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
        inventory: $inventory,
        discoveredClasses: spec195DiscoveredClasses(),
        baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
    );

    expect(spec195FormattedIssues($issues))
        ->toContain(\App\Filament\Pages\ChooseEnvironment::class)
        ->toContain('reason category is invalid or missing');
});

it('fails when a residual surface loses its structured evidence', function (): void {
    $inventory = ActionSurfaceExemptions::spec195ResidualSurfaceInventory();
    $inventory[\App\Filament\System\Pages\Directory\ViewTenant::class]['evidence'] = [];

    $issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
        inventory: $inventory,
        discoveredClasses: spec195DiscoveredClasses(),
        baselineExemptions: ActionSurfaceExemptions::baseline()->all(),
    );

    expect(spec195FormattedIssues($issues))
        ->toContain(\App\Filament\System\Pages\Directory\ViewTenant::class)
        ->toContain('require at least one structured evidence descriptor');
});

it('fails when a retired surface is reintroduced as a stale baseline exemption', function (): void {
    $baselineExemptions = ActionSurfaceExemptions::baseline()->all();
    $baselineExemptions[\App\Filament\Pages\BreakGlassRecovery::class] = 'Stale retired page exemption.';

    $issues = ActionSurfaceValidator::validateSpec195ResidualInventoryFixture(
        inventory: ActionSurfaceExemptions::spec195ResidualSurfaceInventory(),
        discoveredClasses: spec195DiscoveredClasses(),
        baselineExemptions: $baselineExemptions,
    );

    expect(spec195FormattedIssues($issues))
        ->toContain(\App\Filament\Pages\BreakGlassRecovery::class)
        ->toContain('must not remain baseline-exempt');
});
