<?php

declare(strict_types=1);

use App\Filament\Resources\EvidenceSnapshotResource\Pages\ListEvidenceSnapshots;
use App\Filament\Resources\ReviewPackResource\Pages\ListReviewPacks;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\ReviewPack;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Navigation\CrossResourceNavigationMatrix;
use App\Support\Navigation\RelatedNavigationResolver;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OpsUx\OperationUxPresenter;
use App\Support\ReviewPackStatus;
use App\Support\Ui\DerivedState\DerivedStateFamily;
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

it('refreshes evidence artifact truth after expiring a snapshot in the same request', function (): void {
    $tenant = \App\Models\ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $snapshot = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'completeness_state' => EvidenceCompletenessState::Complete->value,
        'summary' => [
            'dimension_count' => 5,
            'missing_dimensions' => 0,
            'stale_dimensions' => 0,
        ],
        'generated_at' => now(),
    ]);

    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ListEvidenceSnapshots::class)
        ->callTableAction('expire', $snapshot, [
            'expiration_reason' => 'This snapshot is no longer valid for governance use.',
        ])
        ->assertHasNoTableActionErrors();

    $snapshot->refresh();
    $truth = app(ArtifactTruthPresenter::class)->forEvidenceSnapshot($snapshot);

    expect((string) $snapshot->status)->toBe(EvidenceSnapshotStatus::Expired->value)
        ->and($truth->freshnessState)->toBe('stale')
        ->and(app(RequestScopedDerivedStateStore::class)->countStored(
            DerivedStateFamily::ArtifactTruth,
            EvidenceSnapshot::class,
            (string) $snapshot->getKey(),
            'evidence_snapshot',
        ))->toBe(1);
});

it('refreshes review-pack artifact truth after expiring a pack in the same request', function (): void {
    $tenant = \App\Models\ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $pack = ReviewPack::factory()->ready()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'initiated_by_user_id' => (int) $user->getKey(),
        'file_disk' => 'exports',
        'file_path' => 'review-packs/freshness.zip',
    ]);

    \Illuminate\Support\Facades\Storage::fake('exports');
    \Illuminate\Support\Facades\Storage::disk('exports')->put('review-packs/freshness.zip', 'PK-fake');

    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ListReviewPacks::class)
        ->callTableAction('expire', $pack);

    $pack->refresh();
    $truth = app(ArtifactTruthPresenter::class)->forReviewPack($pack);

    expect((string) $pack->status)->toBe(ReviewPackStatus::Expired->value)
        ->and($truth->freshnessState)->toBe('stale')
        ->and(app(RequestScopedDerivedStateStore::class)->countStored(
            DerivedStateFamily::ArtifactTruth,
            ReviewPack::class,
            (string) $pack->getKey(),
            'review_pack',
        ))->toBe(1);
});

it('returns fresh operation guidance after run state changes within the same request', function (): void {
    $tenant = \App\Models\ManagedEnvironment::factory()->create();
    createUserWithTenant(tenant: $tenant, role: 'owner');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'inventory_sync',
        'status' => OperationRunStatus::Queued->value,
        'outcome' => OperationRunOutcome::Pending->value,
    ]);

    $queuedGuidance = OperationUxPresenter::surfaceGuidance($run);

    $run->update([
        'status' => OperationRunStatus::Completed->value,
        'outcome' => OperationRunOutcome::Blocked->value,
        'context' => [
            'reason_code' => 'missing_capability',
            'execution_legitimacy' => [
                'reason_code' => 'missing_capability',
            ],
        ],
        'failure_summary' => [[
            'reason_code' => 'missing_capability',
            'message' => 'Missing capability prevented execution.',
        ]],
    ]);

    $staleGuidance = OperationUxPresenter::surfaceGuidance($run->fresh());
    $freshGuidance = OperationUxPresenter::surfaceGuidanceFresh($run->fresh());

    expect($staleGuidance)->toBe($queuedGuidance)
        ->and($freshGuidance)->not->toBe($staleGuidance)
        ->and($freshGuidance)->toBe('Review the blocked prerequisite before retrying.')
        ->and(app(RequestScopedDerivedStateStore::class)->countStored(
            DerivedStateFamily::OperationUxGuidance,
            OperationRun::class,
            (string) $run->getKey(),
            'surface_guidance',
        ))->toBe(1);
});

it('returns fresh related navigation after a finding changes its related policy version', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

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

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

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

    $finding = Finding::factory()->for($tenant)->create([
        'evidence_jsonb' => [
            'current' => [
                'policy_version_id' => (int) $versionA->getKey(),
            ],
        ],
    ]);

    $resolver = app(RelatedNavigationResolver::class);
    $firstEntries = collect($resolver->detailEntries(CrossResourceNavigationMatrix::SOURCE_FINDING, $finding));
    $first = $firstEntries->firstWhere('key', 'current_policy_version');

    $finding->update([
        'evidence_jsonb' => [
            'current' => [
                'policy_version_id' => (int) $versionB->getKey(),
            ],
        ],
    ]);

    $staleEntries = collect($resolver->detailEntries(CrossResourceNavigationMatrix::SOURCE_FINDING, $finding->fresh()));
    $freshEntries = collect($resolver->detailEntriesFresh(CrossResourceNavigationMatrix::SOURCE_FINDING, $finding->fresh()));
    $stale = $staleEntries->firstWhere('key', 'current_policy_version');
    $fresh = $freshEntries->firstWhere('key', 'current_policy_version');

    expect($first['targetUrl'] ?? null)->toBe($stale['targetUrl'] ?? null)
        ->and($fresh['targetUrl'] ?? null)->not->toBe($stale['targetUrl'] ?? null)
        ->and($fresh['targetUrl'] ?? null)->toContain((string) $versionB->getKey())
        ->and(app(RequestScopedDerivedStateStore::class)->countStored(
            DerivedStateFamily::RelatedNavigationDetail,
            Finding::class,
            (string) $finding->getKey(),
            CrossResourceNavigationMatrix::SOURCE_FINDING,
        ))->toBe(1);
});
