<?php

declare(strict_types=1);

use App\Filament\Resources\FindingResource\Pages\ViewFinding;
use App\Models\AuditLog;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\User;
use App\Services\Findings\FindingExceptionService;
use App\Support\Audit\AuditActionId;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

it('revokes an approved exception from the tenant finding surface and keeps the audit trail intelligible', function (): void {
    [$requester, $tenant] = createUserWithTenant(role: 'owner');
    $approver = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');

    $finding = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
    ]);

    /** @var FindingExceptionService $service */
    $service = app(FindingExceptionService::class);

    $requested = $service->request($finding, $tenant, $requester, [
        'owner_user_id' => (int) $requester->getKey(),
        'request_reason' => 'Temporary exception while remediation is scheduled.',
        'review_due_at' => now()->addDays(5)->toDateTimeString(),
        'expires_at' => now()->addDays(30)->toDateTimeString(),
        'evidence_references' => [
            [
                'label' => 'Initial review note',
                'source_type' => 'review_pack',
                'source_id' => 'rp-initial',
                'source_fingerprint' => 'fp-initial',
                'measured_at' => now()->subDay()->toDateTimeString(),
            ],
        ],
    ]);

    $service->approve($requested, $approver, [
        'effective_from' => now()->subDay()->toDateTimeString(),
        'expires_at' => now()->addDays(30)->toDateTimeString(),
        'approval_reason' => 'Approved with compensating controls.',
    ]);

    $this->actingAs($requester);
    Filament::setTenant($tenant, true);

    Livewire::test(ViewFinding::class, ['record' => $finding->getKey()])
        ->assertActionVisible('revoke_exception')
        ->callAction('revoke_exception', data: [
            'revocation_reason' => 'Compensating controls no longer exist in production.',
        ])
        ->assertHasNoActionErrors()
        ->assertNotified('Exception revoked');

    $revoked = FindingException::query()
        ->with(['currentDecision', 'decisions', 'evidenceReferences'])
        ->where('finding_id', (int) $finding->getKey())
        ->firstOrFail();

    expect($revoked->status)->toBe(FindingException::STATUS_REVOKED)
        ->and($revoked->current_validity_state)->toBe(FindingException::VALIDITY_REVOKED)
        ->and($revoked->currentDecision?->decision_type)->toBe(FindingExceptionDecision::TYPE_REVOKED)
        ->and($revoked->decisions->pluck('decision_type')->all())->toBe([
            FindingExceptionDecision::TYPE_REQUESTED,
            FindingExceptionDecision::TYPE_APPROVED,
            FindingExceptionDecision::TYPE_REVOKED,
        ])
        ->and($revoked->revocation_reason)->toBe('Compensating controls no longer exist in production.')
        ->and($revoked->revoked_at)->not->toBeNull()
        ->and($revoked->evidenceReferences)->toHaveCount(1)
        ->and($finding->fresh()?->status)->toBe(Finding::STATUS_RISK_ACCEPTED)
        ->and(AuditLog::query()
            ->where('action', AuditActionId::FindingExceptionRevoked->value)
            ->where('resource_type', 'finding_exception')
            ->where('resource_id', (string) $revoked->getKey())
            ->exists())->toBeTrue();

    Livewire::test(ViewFinding::class, ['record' => $finding->getKey()])
        ->assertSee('Risk governance')
        ->assertSee('Revoked');
});
