<?php

declare(strict_types=1);

use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Services\Evidence\EvidenceSnapshotService;
use App\Services\Evidence\Sources\FindingsSummarySource;
use App\Services\ReviewPackService;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Findings\FindingOutcomeSemantics;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

function seedFindingOutcomeMatrix(\App\Models\ManagedEnvironment $tenant): array
{
    return [
        'pending_verification' => Finding::factory()->for($tenant)->create([
            'status' => Finding::STATUS_RESOLVED,
            'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
        ]),
        'verified_cleared' => Finding::factory()->for($tenant)->create([
            'status' => Finding::STATUS_RESOLVED,
            'resolved_reason' => Finding::RESOLVE_REASON_NO_LONGER_DRIFTING,
        ]),
        'closed_duplicate' => Finding::factory()->for($tenant)->create([
            'status' => Finding::STATUS_CLOSED,
            'closed_reason' => Finding::CLOSE_REASON_DUPLICATE,
        ]),
        'risk_accepted' => Finding::factory()->for($tenant)->create([
            'status' => Finding::STATUS_RISK_ACCEPTED,
            'closed_reason' => Finding::CLOSE_REASON_ACCEPTED_RISK,
        ]),
    ];
}

function materializeFindingOutcomeSnapshot(\App\Models\ManagedEnvironment $tenant): EvidenceSnapshot
{
    $payload = app(EvidenceSnapshotService::class)->buildSnapshotPayload($tenant);

    $snapshot = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'status' => EvidenceSnapshotStatus::Active->value,
        'fingerprint' => $payload['fingerprint'],
        'completeness_state' => $payload['completeness'],
        'summary' => $payload['summary'],
        'generated_at' => now(),
    ]);

    foreach ($payload['items'] as $item) {
        $snapshot->items()->create([
            'managed_environment_id' => (int) $tenant->getKey(),
            'workspace_id' => (int) $tenant->workspace_id,
            'dimension_key' => $item['dimension_key'],
            'state' => $item['state'],
            'required' => $item['required'],
            'source_kind' => $item['source_kind'],
            'source_record_type' => $item['source_record_type'],
            'source_record_id' => $item['source_record_id'],
            'source_fingerprint' => $item['source_fingerprint'],
            'measured_at' => $item['measured_at'],
            'freshness_at' => $item['freshness_at'],
            'summary_payload' => $item['summary_payload'],
            'sort_order' => $item['sort_order'],
        ]);
    }

    return $snapshot->load('items');
}

it('summarizes canonical terminal outcomes and report buckets from findings evidence', function (): void {
    [, $tenant] = createUserWithTenant(role: 'owner');

    $findings = seedFindingOutcomeMatrix($tenant);

    $summary = app(FindingsSummarySource::class)->collect($tenant)['summary_payload'] ?? [];

    expect(data_get($summary, 'outcome_counts.'.FindingOutcomeSemantics::OUTCOME_RESOLVED_PENDING_VERIFICATION))->toBe(1)
        ->and(data_get($summary, 'outcome_counts.'.FindingOutcomeSemantics::OUTCOME_VERIFIED_CLEARED))->toBe(1)
        ->and(data_get($summary, 'outcome_counts.'.FindingOutcomeSemantics::OUTCOME_CLOSED_DUPLICATE))->toBe(1)
        ->and(data_get($summary, 'outcome_counts.'.FindingOutcomeSemantics::OUTCOME_RISK_ACCEPTED))->toBe(1)
        ->and(data_get($summary, 'report_bucket_counts.remediation_pending_verification'))->toBe(1)
        ->and(data_get($summary, 'report_bucket_counts.remediation_verified'))->toBe(1)
        ->and(data_get($summary, 'report_bucket_counts.administrative_closure'))->toBe(1)
        ->and(data_get($summary, 'report_bucket_counts.accepted_risk'))->toBe(1);

    $pendingEntry = collect($summary['entries'] ?? [])->firstWhere('id', (int) $findings['pending_verification']->getKey());
    $verifiedEntry = collect($summary['entries'] ?? [])->firstWhere('id', (int) $findings['verified_cleared']->getKey());

    expect(data_get($pendingEntry, 'terminal_outcome.key'))->toBe(FindingOutcomeSemantics::OUTCOME_RESOLVED_PENDING_VERIFICATION)
        ->and(data_get($pendingEntry, 'terminal_outcome.verification_state'))->toBe(FindingOutcomeSemantics::VERIFICATION_PENDING)
        ->and(data_get($verifiedEntry, 'terminal_outcome.key'))->toBe(FindingOutcomeSemantics::OUTCOME_VERIFIED_CLEARED)
        ->and(data_get($verifiedEntry, 'terminal_outcome.verification_state'))->toBe(FindingOutcomeSemantics::VERIFICATION_VERIFIED);
});

it('propagates finding outcome summaries into evidence snapshots environment reviews and review packs', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    seedFindingOutcomeMatrix($tenant);

    $snapshot = materializeFindingOutcomeSnapshot($tenant);

    expect(data_get($snapshot->summary, 'finding_outcomes.'.FindingOutcomeSemantics::OUTCOME_RESOLVED_PENDING_VERIFICATION))->toBe(1)
        ->and(data_get($snapshot->summary, 'finding_outcomes.'.FindingOutcomeSemantics::OUTCOME_VERIFIED_CLEARED))->toBe(1)
        ->and(data_get($snapshot->summary, 'finding_report_buckets.accepted_risk'))->toBe(1);

    $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);

    expect(data_get($review->summary, 'finding_outcomes.'.FindingOutcomeSemantics::OUTCOME_CLOSED_DUPLICATE))->toBe(1)
        ->and(data_get($review->summary, 'finding_report_buckets.administrative_closure'))->toBe(1);

    setAdminEnvironmentContext($tenant);

    $this->actingAs($user)
        ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
        ->assertOk()
        ->assertSee('Terminal outcomes:')
        ->assertSee('resolved pending verification')
        ->assertSee('verified cleared')
        ->assertSee('closed as duplicate')
        ->assertSee('risk accepted');

    setAdminPanelContext();
    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    Livewire::actingAs($user)
        ->test(ReviewRegister::class)
        ->assertCanSeeTableRecords([$review])
        ->assertSee('Terminal outcomes:')
        ->assertSee('resolved pending verification');

    $pack = app(ReviewPackService::class)->generateFromReview($review, $user, [
        'include_pii' => false,
        'include_operations' => false,
    ]);

    expect(data_get($pack->summary, 'finding_outcomes.'.FindingOutcomeSemantics::OUTCOME_RISK_ACCEPTED))->toBe(1)
        ->and(data_get($pack->summary, 'finding_report_buckets.accepted_risk'))->toBe(1);
});
