<?php

declare(strict_types=1);

use App\Filament\Resources\BaselineSnapshotResource;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Services\Auth\WorkspaceCapabilityResolver;
use App\Support\Navigation\CrossResourceNavigationMatrix;
use App\Support\Navigation\RelatedNavigationResolver;
use App\Support\Ui\DerivedState\DerivedStateFamily;
use App\Support\Ui\DerivedState\DerivedStateKey;
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('reuses finding related navigation and caches deterministic negative results', function (): void {
    [$user, $tenant] = createMinimalUserWithTenant(role: 'owner');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'name' => 'Security Baseline',
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    $policy = Policy::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
    ]);

    $version = PolicyVersion::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'policy_id' => (int) $policy->getKey(),
    ]);

    $run = OperationRun::factory()->minimal()->forTenant($tenant)->create([
        'type' => 'baseline_compare',
    ]);

    $finding = Finding::factory()->for($tenant)->create([
        'current_operation_run_id' => (int) $run->getKey(),
        'evidence_jsonb' => [
            'current' => [
                'policy_version_id' => (int) $version->getKey(),
            ],
            'provenance' => [
                'baseline_profile_id' => (int) $profile->getKey(),
                'baseline_snapshot_id' => (int) $snapshot->getKey(),
                'compare_operation_run_id' => (int) $run->getKey(),
            ],
        ],
    ]);

    $resolver = app(RelatedNavigationResolver::class);
    $first = $resolver->primaryListAction(CrossResourceNavigationMatrix::SOURCE_FINDING, $finding);
    $second = $resolver->primaryListAction(CrossResourceNavigationMatrix::SOURCE_FINDING, $finding);

    expect($first?->targetUrl)->toBe($second?->targetUrl);

    $store = app(RequestScopedDerivedStateStore::class);

    expect($store->countStored(
        DerivedStateFamily::RelatedNavigationPrimary,
        Finding::class,
        (string) $finding->getKey(),
        CrossResourceNavigationMatrix::SOURCE_FINDING,
    ))->toBe(1);

    $orphanedFinding = Finding::factory()->for($tenant)->create([
        'evidence_jsonb' => [],
    ]);

    expect($resolver->primaryListAction(CrossResourceNavigationMatrix::SOURCE_FINDING, $orphanedFinding))->toBeNull()
        ->and($resolver->primaryListAction(CrossResourceNavigationMatrix::SOURCE_FINDING, $orphanedFinding))->toBeNull();

    $negativeKey = DerivedStateKey::fromModel(
        DerivedStateFamily::RelatedNavigationPrimary,
        $orphanedFinding,
        CrossResourceNavigationMatrix::SOURCE_FINDING,
        [
            'source_type' => CrossResourceNavigationMatrix::SOURCE_FINDING,
            'surface' => CrossResourceNavigationMatrix::SURFACE_LIST_ROW,
            'active_tenant_id' => (int) $tenant->getKey(),
            'route_name' => null,
            'user_id' => (int) $user->getKey(),
        ],
    );

    expect($store->resolutionRecord($negativeKey)['negative_result'])->toBeTrue();
});

it('reuses operation-run related context across detail and header consumers', function (): void {
    [$user, $tenant] = createMinimalUserWithTenant(role: 'owner');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $run = OperationRun::factory()->minimal()->forTenant($tenant)->create([
        'type' => 'backup_set.update',
        'context' => [
            'backup_set_id' => 123,
        ],
    ]);

    $resolver = app(RelatedNavigationResolver::class);

    $resolver->detailEntries(CrossResourceNavigationMatrix::SOURCE_OPERATION_RUN, $run);
    $resolver->operationLinks($run, $tenant);

    expect(app(RequestScopedDerivedStateStore::class)->countStored(
        DerivedStateFamily::RelatedNavigationDetail,
        OperationRun::class,
        (string) $run->getKey(),
        CrossResourceNavigationMatrix::SOURCE_OPERATION_RUN,
    ))->toBe(1);
});

it('keeps related-navigation target routes tenant-safe for non-members and capability-limited members', function (): void {
    $workspaceTenant = \App\Models\ManagedEnvironment::factory()->create();
    [$member, $workspaceTenant] = createMinimalUserWithTenant(tenant: $workspaceTenant, role: 'readonly');
    $nonMember = \App\Models\User::factory()->create();

    $profile = BaselineProfile::factory()->active()->create([
        'workspace_id' => (int) $workspaceTenant->workspace_id,
    ]);

    $snapshot = BaselineSnapshot::factory()->create([
        'workspace_id' => (int) $workspaceTenant->workspace_id,
        'baseline_profile_id' => (int) $profile->getKey(),
    ]);

    Filament::setTenant(null, true);

    $this->actingAs($nonMember)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspaceTenant->workspace_id])
        ->get(BaselineSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin'))
        ->assertNotFound();

    $resolver = \Mockery::mock(WorkspaceCapabilityResolver::class);
    $resolver->shouldReceive('isMember')->andReturnTrue();
    $resolver->shouldReceive('can')->andReturnFalse();
    app()->instance(WorkspaceCapabilityResolver::class, $resolver);

    $this->actingAs($member)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspaceTenant->workspace_id])
        ->get(BaselineSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin'))
        ->assertForbidden();
});
