<?php

declare(strict_types=1);

use App\Filament\Pages\Governance\DecisionRegister;
use App\Filament\Pages\Governance\GovernanceInbox;
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\EvidenceSnapshotResource;
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\ManagedEnvironmentLinks;
use App\Support\OperationRunLinks;
use App\Support\Workspaces\WorkspaceContext;
use Livewire\Livewire;

it('Spec315 environment owned workspace hub URLs use environment_id only', function (): void {
    $environment = ManagedEnvironment::factory()->active()->create();

    $urls = [
        OperationRunLinks::index($environment),
        ManagedEnvironmentLinks::operationsUrl($environment),
        ManagedEnvironmentLinks::providerConnectionsUrl($environment),
        CustomerReviewWorkspace::environmentFilterUrl($environment),
        GovernanceInbox::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environment->getKey()]),
        DecisionRegister::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environment->getKey()]),
        FindingExceptionsQueue::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environment->getKey()]),
        route('admin.evidence.overview', ['environment_id' => (int) $environment->getKey()]),
        ReviewRegister::getUrl(panel: 'admin', parameters: ['environment_id' => (int) $environment->getKey()]),
    ];

    foreach ($urls as $url) {
        expect($url)
            ->toContain('environment_id='.(int) $environment->getKey())
            ->not->toContain('managed_environment_id=')
            ->not->toContain('tenant_scope=')
            ->not->toContain('tenant=')
            ->not->toContain('tableFilters');
    }
});

it('Spec315 critical workspace hubs accept environment_id as visible explicit filter', function (): void {
    [$user, $environmentA, $environmentB, $records] = spec315SeedEnvironmentFilterWorkspace();

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

    Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(Operations::class)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $environmentA->getKey())
        ->assertSee('Environment filter:')
        ->assertSee($environmentA->name)
        ->assertCanSeeTableRecords([$records['runA']])
        ->assertCanNotSeeTableRecords([$records['runB']]);

    Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(FindingExceptionsQueue::class)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $environmentA->getKey())
        ->assertSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['exceptionA']])
        ->assertCanNotSeeTableRecords([$records['exceptionB']]);

    Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(ListProviderConnections::class)
        ->assertSee('Environment filter:')
        ->assertSee($environmentA->name)
        ->assertCanSeeTableRecords([$records['connectionA']])
        ->assertCanNotSeeTableRecords([$records['connectionB']]);

    Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(ReviewRegister::class)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $environmentA->getKey())
        ->assertSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['reviewA']->fresh()])
        ->assertCanNotSeeTableRecords([$records['reviewB']->fresh()]);

    Livewire::withQueryParams(['environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(CustomerReviewWorkspace::class)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $environmentA->getKey())
        ->assertSee('Environment filter:')
        ->assertCanSeeTableRecords([$environmentA->fresh()])
        ->assertCanNotSeeTableRecords([$environmentB->fresh()]);

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

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

    $this->get(route('admin.evidence.overview', ['environment_id' => (int) $environmentA->getKey()]))
        ->assertOk()
        ->assertSee('Environment filter:')
        ->assertSee($environmentA->name)
        ->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $records['snapshotA']], tenant: $environmentA, panel: 'admin'), false)
        ->assertDontSee(EvidenceSnapshotResource::getUrl('view', ['record' => $records['snapshotB']], tenant: $environmentB, panel: 'admin'), false);
});

it('Spec315 legacy environment query params do not apply workspace hub filters', function (): void {
    [$user, $environmentA, $environmentB, $records] = spec315SeedEnvironmentFilterWorkspace();

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

    Livewire::withQueryParams(['managed_environment_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(Operations::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['runA'], $records['runB']]);

    Livewire::withQueryParams(['tenant_id' => (int) $environmentA->getKey()])
        ->actingAs($user)
        ->test(Operations::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['runA'], $records['runB']]);

    Livewire::withQueryParams(['tenant' => (string) $environmentA->getKey()])
        ->actingAs($user)
        ->test(ReviewRegister::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['reviewA']->fresh(), $records['reviewB']->fresh()]);

    Livewire::withQueryParams(['environment' => (string) $environmentA->getKey()])
        ->actingAs($user)
        ->test(Operations::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['runA'], $records['runB']]);

    Livewire::withQueryParams(['tenant_scope' => 'all'])
        ->actingAs($user)
        ->test(FindingExceptionsQueue::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['exceptionA'], $records['exceptionB']]);

    Livewire::withQueryParams(['tableFilters' => [
        'managed_environment_id' => ['value' => (string) $environmentA->getKey()],
    ]])
        ->actingAs($user)
        ->test(Operations::class)
        ->assertSet('tableFilters.managed_environment_id.value', null)
        ->assertDontSee('Environment filter:')
        ->assertCanSeeTableRecords([$records['runA'], $records['runB']]);
});

it('Spec317 critical workspace hubs ignore every retired tenant query alias', function (): void {
    [$user, $environmentA, $environmentB, $records] = spec315SeedEnvironmentFilterWorkspace();

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

    $legacyQueries = [
        'tenant' => ['tenant' => (string) $environmentA->getKey()],
        'tenant_id' => ['tenant_id' => (int) $environmentA->getKey()],
        'managed_environment_id' => ['managed_environment_id' => (int) $environmentA->getKey()],
        'tenant_scope' => ['tenant_scope' => 'environment'],
        'environment' => ['environment' => (string) $environmentA->getKey()],
        'tableFilters' => [
            'tableFilters' => [
                'managed_environment_id' => ['value' => (string) $environmentA->getKey()],
            ],
        ],
    ];

    $livewireCases = [
        'operations' => [
            'component' => Operations::class,
            'wide_records' => [$records['runA'], $records['runB']],
        ],
        'finding exceptions queue' => [
            'component' => FindingExceptionsQueue::class,
            'wide_records' => [$records['exceptionA'], $records['exceptionB']],
        ],
        'provider connections' => [
            'component' => ListProviderConnections::class,
            'wide_records' => [$records['connectionA'], $records['connectionB']],
        ],
        'review register' => [
            'component' => ReviewRegister::class,
            'wide_records' => [$records['reviewA']->fresh(), $records['reviewB']->fresh()],
        ],
        'customer review workspace' => [
            'component' => CustomerReviewWorkspace::class,
            'wide_records' => [$environmentA->fresh(), $environmentB->fresh()],
        ],
        'decision register' => [
            'component' => DecisionRegister::class,
            'wide_records' => [$records['exceptionA'], $records['exceptionB']],
        ],
    ];

    foreach ($legacyQueries as $queryName => $query) {
        foreach ($livewireCases as $caseName => $case) {
            Livewire::withQueryParams($query)
                ->actingAs($user)
                ->test($case['component'])
                ->assertDontSee('Environment filter:')
                ->assertCanSeeTableRecords($case['wide_records']);
        }

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

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

        $this->get(route('admin.evidence.overview', $query))
            ->assertOk()
            ->assertDontSee('Environment filter:')
            ->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $records['snapshotA']], tenant: $environmentA, panel: 'admin'), false)
            ->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $records['snapshotB']], tenant: $environmentB, panel: 'admin'), false);
    }
});

it('Spec315 environment filter must belong to the current workspace', function (): void {
    $environmentA = ManagedEnvironment::factory()->active()->create();
    [$user, $environmentA] = createUserWithTenant(tenant: $environmentA, role: 'owner');

    $foreignEnvironment = ManagedEnvironment::factory()->active()->create();
    createUserWithTenant(tenant: $foreignEnvironment, user: $user, role: 'owner');

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $environmentA->workspace_id])
        ->get(route('admin.operations.index', [
            'workspace' => $environmentA->workspace,
            'environment_id' => (int) $foreignEnvironment->getKey(),
        ]))
        ->assertNotFound();
});

function spec315SeedEnvironmentFilterWorkspace(): array
{
    $environmentA = ManagedEnvironment::factory()->active()->create(['name' => 'Spec315 Environment A']);
    [$user, $environmentA] = createUserWithTenant(tenant: $environmentA, role: 'owner', workspaceRole: 'manager');

    $environmentB = ManagedEnvironment::factory()->active()->create([
        'workspace_id' => (int) $environmentA->workspace_id,
        'name' => 'Spec315 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 = spec315FindingException($environmentA, $user, 'Spec315 Governance A', 'Spec315 Decision A');
    $exceptionB = spec315FindingException($environmentB, $user, 'Spec315 Governance B', 'Spec315 Decision B');

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

    $snapshotA = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $environmentA->getKey(),
        'workspace_id' => (int) $environmentA->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
        'generated_at' => now(),
    ]);
    $snapshotB = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $environmentB->getKey(),
        'workspace_id' => (int) $environmentB->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'summary' => ['missing_dimensions' => 0, 'stale_dimensions' => 0],
        'generated_at' => now(),
    ]);

    $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 spec315FindingException(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']);
}
