<?php

declare(strict_types=1);

use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\ManagedEnvironment;
use App\Support\ReviewPackStatus;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthPresenter;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('maps stored reports into immutable reference, lifecycle, and retention truth', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $historical = StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'fingerprint' => 'permission-posture-v1',
        'created_at' => now()->subDay(),
        'updated_at' => now()->subDay(),
    ]);

    $current = StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'fingerprint' => 'permission-posture-v2',
        'previous_fingerprint' => 'permission-posture-v1',
        'created_at' => now(),
        'updated_at' => now(),
    ]);

    $historicalTruth = app(ArtifactTruthPresenter::class)->for($historical->fresh());
    $currentTruth = app(ArtifactTruthPresenter::class)->for($current->fresh());

    expect($historicalTruth)->not->toBeNull()
        ->and($currentTruth)->not->toBeNull();

    $historicalState = $historicalTruth?->toArray();
    $currentState = $currentTruth?->toArray();

    expect($historicalState['displayReference'] ?? null)
        ->toContain('Stored report')
        ->toContain('#'.$historical->getKey())
        ->and($historicalState['integrityAnchor'] ?? null)->toBe('permission-posture-v1')
        ->and($historicalState['lifecycleState'] ?? null)->toBe('historical')
        ->and($historicalState['retentionState'] ?? null)->toBe('retained')
        ->and($currentState['displayReference'] ?? null)->toContain('#'.$current->getKey())
        ->and($currentState['integrityAnchor'] ?? null)->toBe('permission-posture-v2')
        ->and($currentState['lifecycleState'] ?? null)->toBe('current')
        ->and($currentState['retentionState'] ?? null)->toBe('retained');
});

it('maps accepted-risk decision history into current and superseded artifact truth', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $finding = Finding::factory()->for($tenant)->create();

    $exception = FindingException::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'finding_id' => (int) $finding->getKey(),
        'requested_by_user_id' => (int) $user->getKey(),
        'owner_user_id' => (int) $user->getKey(),
        'status' => FindingException::STATUS_ACTIVE,
        'current_validity_state' => FindingException::VALIDITY_VALID,
        'request_reason' => 'Approve temporary exception',
        'requested_at' => now()->subDays(10),
        'approved_at' => now()->subDays(9),
        'effective_from' => now()->subDays(9),
        'expires_at' => now()->addDays(14),
        'review_due_at' => now()->addWeek(),
        'evidence_summary' => ['reference_count' => 1],
    ]);

    $requestedDecision = $exception->decisions()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'actor_user_id' => (int) $user->getKey(),
        'decision_type' => FindingExceptionDecision::TYPE_REQUESTED,
        'reason' => 'Approve temporary exception',
        'metadata' => [],
        'decided_at' => now()->subDays(10),
    ]);

    $approvedDecision = $exception->decisions()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'actor_user_id' => (int) $user->getKey(),
        'decision_type' => FindingExceptionDecision::TYPE_APPROVED,
        'reason' => 'Approved for a bounded review period',
        'metadata' => [],
        'effective_from' => now()->subDays(9),
        'expires_at' => now()->addDays(14),
        'decided_at' => now()->subDays(9),
    ]);

    $exception->forceFill(['current_decision_id' => (int) $approvedDecision->getKey()])->save();

    $requestedTruth = app(ArtifactTruthPresenter::class)->for($requestedDecision->fresh('exception.currentDecision'));
    $approvedTruth = app(ArtifactTruthPresenter::class)->for($approvedDecision->fresh('exception.currentDecision'));

    expect($requestedTruth)->not->toBeNull()
        ->and($approvedTruth)->not->toBeNull();

    $requestedState = $requestedTruth?->toArray();
    $approvedState = $approvedTruth?->toArray();

    expect($requestedState['displayReference'] ?? null)
        ->toContain('Accepted-risk decision')
        ->toContain('#'.$requestedDecision->getKey())
        ->and($requestedState['lifecycleState'] ?? null)->toBe('superseded')
        ->and($requestedState['retentionState'] ?? null)->toBe('retained')
        ->and($approvedState['displayReference'] ?? null)->toContain('#'.$approvedDecision->getKey())
        ->and($approvedState['lifecycleState'] ?? null)->toBe('current')
        ->and($approvedState['retentionState'] ?? null)->toBe('retained');
});

it('keeps expired direct access separate from historical lifecycle on review packs', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $pack = ReviewPack::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'initiated_by_user_id' => (int) $user->getKey(),
        'status' => ReviewPackStatus::Expired->value,
        'fingerprint' => 'review-pack-expired',
        'sha256' => 'sha-expired-pack',
        'generated_at' => now()->subDays(3),
        'expires_at' => now()->subDay(),
    ]);

    $truth = app(ArtifactTruthPresenter::class)->forReviewPack($pack->fresh());
    $state = $truth->toArray();

    expect($state['displayReference'] ?? null)->not->toBeNull()
        ->and($state['displayReference'])->toContain('Review pack')
        ->and($state['displayReference'])->toContain('#'.$pack->getKey())
        ->and($state['integrityAnchor'] ?? null)->toBe('sha-expired-pack')
        ->and($state['lifecycleState'] ?? null)->toBe('historical')
        ->and($state['retentionState'] ?? null)->toBe('expired_direct_access');
});
