<?php

declare(strict_types=1);

use App\Filament\Pages\Governance\DecisionRegister;
use App\Filament\Pages\Governance\GovernanceInbox;
use App\Filament\Pages\Monitoring\EvidenceOverview;
use App\Filament\Pages\Monitoring\FindingExceptionsQueue;
use App\Filament\Pages\Monitoring\Operations;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Resources\ProviderConnectionResource;
use App\Filament\Resources\ProviderConnectionResource\Pages\ListProviderConnections;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\ManagedEnvironment;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\User;
use App\Support\EnvironmentReviewStatus;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Navigation\WorkspaceHubFilterStateResetter;
use App\Support\Navigation\WorkspaceHubRegistry;
use App\Support\OperationRunLinks;
use App\Support\Workspaces\WorkspaceContext;
use Livewire\Livewire;

it('Spec316 resetter removes environment-like persisted table filters without deleting unrelated filters', function (): void {
    $filtersSessionKey = 'filament.spec316.filters';

    session()->put($filtersSessionKey, [
        'tenant' => ['value' => 'legacy-tenant'],
        'tenant_id' => ['value' => '101'],
        'managed_environment_id' => ['value' => '101'],
        'environment_id' => ['value' => '101'],
        'environment' => ['value' => 'legacy-environment'],
        'tenant_scope' => ['value' => 'environment'],
        'tableFilters' => [
            'tenant' => ['value' => 'legacy-tenant'],
            'managed_environment_id' => ['value' => '101'],
            'status' => ['value' => 'pending'],
        ],
        'status' => ['value' => 'pending'],
    ]);

    app(WorkspaceHubFilterStateResetter::class)
        ->forgetPersistedEnvironmentLikeFilters($filtersSessionKey, request());

    $persistedFilters = session()->get($filtersSessionKey);

    expect($persistedFilters)
        ->toMatchArray([
            'status' => ['value' => 'pending'],
            'tableFilters' => [
                'status' => ['value' => 'pending'],
            ],
        ]);

    foreach (WorkspaceHubRegistry::environmentLikeFilterKeys() as $filterName) {
        expect(data_get($persistedFilters, "{$filterName}.value"))->toBeNull()
            ->and(data_get($persistedFilters, "tableFilters.{$filterName}.value"))->toBeNull();
    }
});

it('Spec316 clear filter result is reload safe across table backed workspace hubs', function (): void {
    [$user, $environmentA, $environmentB, $records] = spec316ClearFilterWorkspace();

    $this->actingAs($user);
    setAdminPanelContext();
    session()->put(WorkspaceContext::SESSION_KEY, (int) $environmentA->workspace_id);
    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $environmentA->workspace_id => (int) $environmentA->getKey(),
    ]);

    $cases = [
        'operations' => [
            'component' => Operations::class,
            'clean_url' => OperationRunLinks::index(),
            'filtered_records' => [$records['runA']],
            'hidden_records' => [$records['runB']],
            'wide_records' => [$records['runA'], $records['runB']],
            'session_environment_value' => (string) $environmentA->getKey(),
        ],
        'finding exceptions queue' => [
            'component' => FindingExceptionsQueue::class,
            'clean_url' => FindingExceptionsQueue::getUrl(panel: 'admin'),
            'filtered_records' => [$records['exceptionA']],
            'hidden_records' => [$records['exceptionB']],
            'wide_records' => [$records['exceptionA'], $records['exceptionB']],
            'session_environment_value' => (string) $environmentA->getKey(),
        ],
        'provider connections' => [
            'component' => ListProviderConnections::class,
            'clean_url' => ProviderConnectionResource::getUrl('index', panel: 'admin'),
            'filtered_records' => [$records['connectionA']],
            'hidden_records' => [$records['connectionB']],
            'wide_records' => [$records['connectionA'], $records['connectionB']],
            'session_environment_value' => (string) $environmentA->external_id,
        ],
        'evidence overview' => [
            'component' => EvidenceOverview::class,
            'clean_url' => route('admin.evidence.overview'),
            'filtered_records' => [(string) $records['snapshotA']->getKey()],
            'hidden_records' => [(string) $records['snapshotB']->getKey()],
            'wide_records' => [(string) $records['snapshotA']->getKey(), (string) $records['snapshotB']->getKey()],
            'session_environment_value' => (string) $environmentA->getKey(),
        ],
        'review register' => [
            'component' => ReviewRegister::class,
            'clean_url' => ReviewRegister::getUrl(panel: 'admin'),
            'filtered_records' => [$records['reviewA']->fresh()],
            'hidden_records' => [$records['reviewB']->fresh()],
            'wide_records' => [$records['reviewA']->fresh(), $records['reviewB']->fresh()],
            'session_environment_value' => (string) $environmentA->getKey(),
        ],
        'customer review workspace' => [
            'component' => CustomerReviewWorkspace::class,
            'clean_url' => CustomerReviewWorkspace::getUrl(panel: 'admin'),
            'filtered_records' => [$environmentA->fresh()],
            'hidden_records' => [$environmentB->fresh()],
            'wide_records' => [$environmentA->fresh(), $environmentB->fresh()],
            'session_environment_value' => (string) $environmentA->getKey(),
        ],
    ];

    foreach ($cases as $case) {
        $filteredComponent = Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
            ->actingAs($user)
            ->test($case['component'])
            ->assertSee('Environment filter:')
            ->assertSee('Clear filter')
            ->assertCanSeeTableRecords($case['filtered_records'])
            ->assertCanNotSeeTableRecords($case['hidden_records']);

        $filtersSessionKey = $filteredComponent->instance()->getTableFiltersSessionKey();
        spec316PersistLegacyEnvironmentFilterState($filtersSessionKey, $case['session_environment_value']);

        $this->get($case['clean_url'])
            ->assertOk()
            ->assertDontSee('Environment filter:');

        spec316AssertEnvironmentLikeFiltersForgotten($filtersSessionKey);

        $this->get($case['clean_url'])
            ->assertOk()
            ->assertDontSee('Environment filter:');

        Livewire::withQueryParams([])
            ->actingAs($user)
            ->test($case['component'])
            ->assertDontSee('Environment filter:')
            ->assertCanSeeTableRecords($case['wide_records']);
    }
});

it('Spec316 clear filter result is clean for governance and decision workspace hubs', function (): void {
    [$user, $environmentA, $environmentB, $records] = spec316ClearFilterWorkspace();

    $this->actingAs($user);
    setAdminPanelContext();
    session()->put(WorkspaceContext::SESSION_KEY, (int) $environmentA->workspace_id);

    $this->get(GovernanceInbox::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environmentA->getKey()]))
        ->assertOk()
        ->assertSee('Environment filter:')
        ->assertSee($environmentA->name)
        ->assertDontSee('Spec316 Governance B');

    $this->get(GovernanceInbox::getUrl(panel: 'admin'))
        ->assertOk()
        ->assertDontSee('Environment filter:')
        ->assertSee('Spec316 Governance B');

    $this->get(DecisionRegister::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environmentA->getKey()]))
        ->assertOk()
        ->assertSee('Environment filter:')
        ->assertSee($environmentA->name)
        ->assertDontSee('Spec316 Decision B');

    $this->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertOk()
        ->assertDontSee('Environment filter:');

    Livewire::withQueryParams([])
        ->actingAs($user)
        ->test(DecisionRegister::class)
        ->assertCanSeeTableRecords([$records['exceptionA'], $records['exceptionB']]);

    expect($environmentB->workspace_id)->toBe($environmentA->workspace_id);
});

/**
 * @return array{0: User, 1: ManagedEnvironment, 2: ManagedEnvironment, 3: array<string, mixed>}
 */
function spec316ClearFilterWorkspace(): array
{
    $environmentA = ManagedEnvironment::factory()->active()->create([
        'name' => 'Spec316 Environment A',
        'external_id' => 'spec316-environment-a',
    ]);
    [$user, $environmentA] = createUserWithTenant(tenant: $environmentA, role: 'owner', workspaceRole: 'manager');

    $environmentB = ManagedEnvironment::factory()->active()->create([
        'workspace_id' => (int) $environmentA->workspace_id,
        'name' => 'Spec316 Environment B',
        'external_id' => 'spec316-environment-b',
    ]);
    createUserWithTenant(tenant: $environmentB, user: $user, role: 'owner', workspaceRole: 'manager');

    $runA = OperationRun::factory()->forTenant($environmentA)->create(['type' => 'policy.sync']);
    $runB = OperationRun::factory()->forTenant($environmentB)->create(['type' => 'inventory_sync']);

    $exceptionA = spec316ClearFilterFindingException($environmentA, $user, 'Spec316 Governance A', 'Spec316 Decision A');
    $exceptionB = spec316ClearFilterFindingException($environmentB, $user, 'Spec316 Governance B', 'Spec316 Decision B');

    $connectionA = ProviderConnection::factory()->create([
        'workspace_id' => (int) $environmentA->workspace_id,
        'managed_environment_id' => (int) $environmentA->getKey(),
        'display_name' => 'Spec316 Provider A',
    ]);
    $connectionB = ProviderConnection::factory()->create([
        'workspace_id' => (int) $environmentB->workspace_id,
        'managed_environment_id' => (int) $environmentB->getKey(),
        'display_name' => 'Spec316 Provider B',
    ]);

    $snapshotA = spec316ClearFilterEvidenceSnapshot($environmentA);
    $snapshotB = spec316ClearFilterEvidenceSnapshot($environmentB);

    $reviewA = composeEnvironmentReviewForTest($environmentA, $user, $snapshotA);
    $reviewA->forceFill([
        'status' => EnvironmentReviewStatus::Published->value,
        'published_at' => now(),
        'published_by_user_id' => (int) $user->getKey(),
    ])->save();

    $reviewB = composeEnvironmentReviewForTest($environmentB, $user, $snapshotB);
    $reviewB->forceFill([
        'status' => EnvironmentReviewStatus::Published->value,
        'published_at' => now(),
        'published_by_user_id' => (int) $user->getKey(),
    ])->save();

    return [$user, $environmentA, $environmentB, compact(
        'runA',
        'runB',
        'exceptionA',
        'exceptionB',
        'connectionA',
        'connectionB',
        'snapshotA',
        'snapshotB',
        'reviewA',
        'reviewB',
    )];
}

function spec316ClearFilterFindingException(
    ManagedEnvironment $environment,
    User $actor,
    string $requestReason,
    string $decisionReason,
): FindingException {
    $finding = Finding::factory()->for($environment)->riskAccepted()->create([
        'workspace_id' => (int) $environment->workspace_id,
        'subject_external_id' => str()->slug($requestReason),
    ]);

    $exception = FindingException::query()->create([
        'workspace_id' => (int) $environment->workspace_id,
        'managed_environment_id' => (int) $environment->getKey(),
        'finding_id' => (int) $finding->getKey(),
        'requested_by_user_id' => (int) $actor->getKey(),
        'owner_user_id' => (int) $actor->getKey(),
        'status' => FindingException::STATUS_PENDING,
        'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
        'request_reason' => $requestReason,
        'requested_at' => now()->subDay(),
        'review_due_at' => now()->addDay(),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    $decision = $exception->decisions()->create([
        'workspace_id' => (int) $environment->workspace_id,
        'managed_environment_id' => (int) $environment->getKey(),
        'actor_user_id' => (int) $actor->getKey(),
        'decision_type' => FindingExceptionDecision::TYPE_REQUESTED,
        'reason' => $decisionReason,
        'metadata' => [],
        'decided_at' => now()->subDay(),
    ]);

    $exception->forceFill(['current_decision_id' => (int) $decision->getKey()])->save();

    return $exception->fresh(['currentDecision']);
}

function spec316ClearFilterEvidenceSnapshot(ManagedEnvironment $environment): EvidenceSnapshot
{
    return EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $environment->getKey(),
        'workspace_id' => (int) $environment->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
        'generated_at' => now(),
    ]);
}

function spec316PersistLegacyEnvironmentFilterState(string $filtersSessionKey, string $environmentValue): void
{
    session()->put($filtersSessionKey, [
        'tenant' => ['value' => $environmentValue],
        'tenant_id' => ['value' => $environmentValue],
        'managed_environment_id' => ['value' => $environmentValue],
        'environment_id' => ['value' => $environmentValue],
        'environment' => ['value' => $environmentValue],
        'tenant_scope' => ['value' => 'environment'],
        'tableFilters' => [
            'tenant' => ['value' => $environmentValue],
            'managed_environment_id' => ['value' => $environmentValue],
        ],
    ]);
}

function spec316AssertEnvironmentLikeFiltersForgotten(string $filtersSessionKey): void
{
    $persistedFilters = session()->get($filtersSessionKey, []);

    foreach (WorkspaceHubRegistry::environmentLikeFilterKeys() as $filterName) {
        expect(data_get($persistedFilters, "{$filterName}.value"))->toBeNull()
            ->and(data_get($persistedFilters, "tableFilters.{$filterName}.value"))->toBeNull();
    }
}
