<?php

declare(strict_types=1);

use App\Filament\Resources\EvidenceSnapshotResource;
use App\Filament\Resources\EvidenceSnapshotResource\Pages\ListEvidenceSnapshots;
use App\Filament\Resources\EvidenceSnapshotResource\Pages\ViewEvidenceSnapshot;
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Jobs\GenerateEvidenceSnapshotJob;
use App\Models\EvidenceSnapshot;
use App\Models\EvidenceSnapshotItem;
use App\Models\Finding;
use App\Models\OperationRun;
use App\Models\PlatformUser;
use App\Models\ReviewPack;
use App\Models\StoredReport;
use App\Models\ManagedEnvironment;
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
use App\Services\Settings\SettingsWriter;
use App\Support\Auth\Capabilities;
use App\Support\Auth\PlatformCapabilities;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Queue;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;
use Tests\Feature\Concerns\BuildsGovernanceArtifactTruthFixtures;

uses(RefreshDatabase::class, BuildsGovernanceArtifactTruthFixtures::class);

function seedEvidenceDomain(ManagedEnvironment $tenant): void
{
    StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_PERMISSION_POSTURE,
        'fingerprint' => hash('sha256', 'permission-'.$tenant->getKey()),
    ]);

    StoredReport::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'report_type' => StoredReport::REPORT_TYPE_ENTRA_ADMIN_ROLES,
        'fingerprint' => hash('sha256', 'entra-'.$tenant->getKey()),
    ]);

    Finding::factory()->count(2)->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
    ]);

    OperationRun::factory()->forTenant($tenant)->create();
}

function evidenceSnapshotHeaderActions(Testable $component): array
{
    $instance = $component->instance();

    if ($instance->getCachedHeaderActions() === []) {
        $instance->cacheInteractsWithHeaderActions();
    }

    return $instance->getCachedHeaderActions();
}

function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
{
    app(SettingsWriter::class)->updateWorkspaceCommercialLifecycle(
        actor: PlatformUser::factory()->create([
            'capabilities' => [
                PlatformCapabilities::ACCESS_SYSTEM_PANEL,
                PlatformCapabilities::DIRECTORY_VIEW,
                PlatformCapabilities::COMMERCIAL_LIFECYCLE_MANAGE,
            ],
            'is_active' => true,
        ]),
        workspace: $tenant->workspace,
        state: WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY,
        reason: 'Evidence read-only preservation test',
    );
}

it('renders the evidence list page for an authorized user', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
        ->assertOk();
});

it('disables evidence global search while keeping the view page available', function (): void {
    $reflection = new ReflectionClass(EvidenceSnapshotResource::class);

    expect($reflection->getStaticPropertyValue('isGloballySearchable'))->toBeFalse()
        ->and(array_keys(EvidenceSnapshotResource::getPages()))->toContain('view');
});

it('returns 404 for non members on the evidence list route', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user] = createUserWithTenant(role: 'owner');

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
        ->assertNotFound();
});

it('returns 403 for tenant members without evidence view capability on the evidence list route', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    Gate::define(Capabilities::EVIDENCE_VIEW, fn (): bool => false);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
        ->assertForbidden();
});

it('disables create snapshot for readonly members', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ListEvidenceSnapshots::class)
        ->assertActionVisible('create_snapshot')
        ->assertActionDisabled('create_snapshot');
});

it('queues snapshot generation from the list header action', function (): void {
    Queue::fake();

    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    seedEvidenceDomain($tenant);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ListEvidenceSnapshots::class)
        ->callAction('create_snapshot')
        ->assertNotified();

    expect(EvidenceSnapshot::query()->count())->toBe(1);
    Queue::assertPushed(GenerateEvidenceSnapshotJob::class);
});

it('renders the view page for an active snapshot', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
    $run = OperationRun::factory()->forTenant($tenant)->create();

    $snapshot = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'operation_run_id' => (int) $run->getKey(),
        'status' => EvidenceSnapshotStatus::Active->value,
        'completeness_state' => EvidenceCompletenessState::Complete->value,
        'summary' => ['finding_count' => 2],
        'generated_at' => now(),
    ]);

    ReviewPack::factory()->ready()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'initiated_by_user_id' => (int) $user->getKey(),
    ]);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
        ->assertOk()
        ->assertSee('Related context')
        ->assertSee('Review pack');

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $refreshRule = GovernanceActionCatalog::rule('refresh_evidence');
    $expireRule = GovernanceActionCatalog::rule('expire_snapshot');

    $refreshComponent = Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionVisible('refresh_evidence')
        ->assertActionExists('refresh_evidence', fn (Action $action): bool => $action->getLabel() === $refreshRule->canonicalLabel
            && $action->isConfirmationRequired()
            && $action->getModalHeading() === $refreshRule->modalHeading
            && $action->getModalDescription() === $refreshRule->modalDescription)
        ->assertActionVisible('expire_snapshot')
        ->assertActionExists('expire_snapshot', fn (Action $action): bool => $action->getLabel() === $expireRule->canonicalLabel
            && $action->isConfirmationRequired()
            && $action->getModalHeading() === $expireRule->modalHeading
            && $action->getModalDescription() === $expireRule->modalDescription)
        ->mountAction('refresh_evidence')
        ->assertActionMounted('refresh_evidence');

    $component = Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()]);

    Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionVisible('expire_snapshot')
        ->mountAction('expire_snapshot')
        ->assertActionMounted('expire_snapshot')
        ->callMountedAction()
        ->assertHasActionErrors(['expiration_reason']);

    expect(collect(evidenceSnapshotHeaderActions($component))
        ->map(static fn ($action): ?string => method_exists($action, 'getName') ? $action->getName() : null)
        ->filter()
        ->values()
        ->all())
        ->toEqualCanonicalizing(['refresh_evidence', 'expire_snapshot'])
        ->and(collect(EvidenceSnapshotResource::relatedContextEntries($snapshot))->pluck('key')->all())
        ->toContain('operation_run', 'review_pack');
});

it('keeps evidence snapshot detail accessible for readonly members while suspended read-only', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');

    $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' => ['finding_count' => 2],
        'generated_at' => now(),
    ]);

    suspendEvidenceSnapshotWorkspace($tenant);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
        ->assertOk();

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionVisible('refresh_evidence')
        ->assertActionDisabled('refresh_evidence')
        ->assertActionVisible('expire_snapshot')
        ->assertActionDisabled('expire_snapshot');
});

it('shows artifact truth and next-step guidance for degraded evidence snapshots', function (): void {
    $tenant = 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::Partial->value,
        'summary' => [
            'dimension_count' => 5,
            'missing_dimensions' => 2,
            'stale_dimensions' => 0,
        ],
        'generated_at' => now(),
    ]);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
        ->assertOk()
        ->assertSee('Outcome summary')
        ->assertDontSee('Artifact truth')
        ->assertSee('Artifact reference')
        ->assertSee('Evidence snapshot #'.$snapshot->getKey())
        ->assertSee('Lifecycle')
        ->assertSee('Current')
        ->assertSee('Retention')
        ->assertSee('Retained')
        ->assertSee('Partially complete')
        ->assertSee('Refresh evidence before using this snapshot');
});

it('shows stale evidence snapshots as cautionary while fresh snapshots remain current', function (): void {
    $freshTenant = ManagedEnvironment::factory()->create();
    [$user, $freshTenant] = createUserWithTenant(tenant: $freshTenant, role: 'owner');
    $staleTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $freshTenant->workspace_id,
    ]);
    createUserWithTenant(tenant: $staleTenant, user: $user, role: 'owner');

    $freshSnapshot = $this->makeArtifactTruthEvidenceSnapshot($freshTenant);
    $staleSnapshot = $this->makeStaleArtifactTruthEvidenceSnapshot($staleTenant);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $freshSnapshot], tenant: $freshTenant, panel: 'admin'))
        ->assertOk()
        ->assertSee('No action needed')
        ->assertDontSee('Refresh the stale evidence before relying on this snapshot');

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'admin'))
        ->assertOk()
        ->assertSee('Refresh recommended')
        ->assertSee('Refresh the stale evidence before relying on this snapshot');
});

it('renders readable evidence dimension summaries and keeps raw json available', function (): void {
    $tenant = 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' => [
            'finding_count' => 3,
            'report_count' => 2,
            'operation_count' => 1,
        ],
        'generated_at' => now(),
    ]);

    EvidenceSnapshotItem::query()->create([
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'dimension_key' => 'findings_summary',
        'state' => EvidenceCompletenessState::Complete->value,
        'required' => true,
        'source_kind' => 'model_summary',
        'summary_payload' => [
            'count' => 3,
            'open_count' => 2,
            'severity_counts' => [
                'critical' => 1,
                'high' => 1,
                'medium' => 1,
                'low' => 0,
            ],
            'entries' => [
                [
                    'title' => 'Admin consent missing',
                    'severity' => 'critical',
                    'status' => 'open',
                ],
            ],
        ],
        'sort_order' => 10,
    ]);

    EvidenceSnapshotItem::query()->create([
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'dimension_key' => 'entra_admin_roles',
        'state' => EvidenceCompletenessState::Complete->value,
        'required' => true,
        'source_kind' => 'stored_report',
        'summary_payload' => [
            'role_count' => 2,
            'roles' => [
                ['display_name' => 'Global Administrator'],
                ['display_name' => 'Security Administrator'],
            ],
        ],
        'sort_order' => 20,
    ]);

    EvidenceSnapshotItem::query()->create([
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'dimension_key' => 'operations_summary',
        'state' => EvidenceCompletenessState::Complete->value,
        'required' => true,
        'source_kind' => 'operation_rollup',
        'summary_payload' => [
            'operation_count' => 3,
            'failed_count' => 0,
            'partial_count' => 0,
            'entries' => [
                [
                    'type' => 'tenant.evidence.snapshot.generate',
                    'outcome' => 'pending',
                    'status' => 'running',
                ],
                [
                    'type' => 'environment.review_pack.generate',
                    'outcome' => 'succeeded',
                    'status' => 'completed',
                ],
                [
                    'type' => 'backup_schedule_purge',
                    'outcome' => 'pending',
                    'status' => 'queued',
                ],
            ],
        ],
        'sort_order' => 30,
    ]);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
        ->assertOk()
        ->assertSeeText('3 findings, 2 open.')
        ->assertSeeText('Open findings')
        ->assertSeeText('Admin consent missing')
        ->assertSeeText('2 privileged Entra roles captured.')
        ->assertSeeText('Global Administrator')
        ->assertSeeText('Evidence snapshot generation · In progress')
        ->assertSeeText('Review pack generation · Completed successfully')
        ->assertSeeText('Backup schedule purge · Queued for execution')
        ->assertDontSeeText('ManagedEnvironment.evidence.snapshot.generate · Pending · Running')
        ->assertSeeText('Copy JSON');
});

it('hides evidence refresh, expiry, operation, fingerprint, and raw json in the customer review proof flow', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
    $run = OperationRun::factory()->forTenant($tenant)->create();

    $snapshot = EvidenceSnapshot::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'operation_run_id' => (int) $run->getKey(),
        'status' => EvidenceSnapshotStatus::Active->value,
        'completeness_state' => EvidenceCompletenessState::Complete->value,
        'summary' => ['finding_count' => 1],
        'fingerprint' => hash('sha256', 'customer-proof-flow'),
        'generated_at' => now(),
    ]);

    EvidenceSnapshotItem::query()->create([
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'dimension_key' => 'findings_summary',
        'state' => EvidenceCompletenessState::Complete->value,
        'required' => true,
        'source_kind' => 'model_summary',
        'summary_payload' => ['count' => 1, 'open_count' => 0],
        'sort_order' => 10,
    ]);

    $this->actingAs($user)
        ->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin').'?'.http_build_query([
            'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
            'review_id' => '456',
            'tenant_filter_id' => (string) $tenant->getKey(),
            'interpretation_version' => 'compliance_evidence_mapping.v1',
        ]))
        ->assertOk()
        ->assertSee('Evidence dimensions')
        ->assertDontSee('Open the latest evidence refresh operation.')
        ->assertDontSee('customer-proof-flow')
        ->assertDontSee('Raw summary JSON')
        ->assertDontSee('Copy JSON');

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::withQueryParams([
        'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
        'review_id' => '456',
        'tenant_filter_id' => (string) $tenant->getKey(),
        'interpretation_version' => 'compliance_evidence_mapping.v1',
    ])
        ->actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionDoesNotExist('refresh_evidence')
        ->assertActionDoesNotExist('expire_snapshot');
});

it('hides expire actions for expired snapshots on list and detail surfaces', function (): void {
    $tenant = 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::Expired->value,
        'completeness_state' => EvidenceCompletenessState::Complete->value,
        'summary' => ['finding_count' => 2],
        'generated_at' => now()->subDay(),
        'expires_at' => now(),
    ]);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ListEvidenceSnapshots::class)
        ->assertCanSeeTableRecords([$snapshot])
        ->assertTableActionHidden('expire', $snapshot);

    Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionHidden('expire_snapshot');
});

it('disables refresh and expire actions for readonly members on snapshot detail', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');

    $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' => ['finding_count' => 2],
        'generated_at' => now(),
    ]);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    Livewire::actingAs($user)
        ->test(ViewEvidenceSnapshot::class, ['record' => $snapshot->getKey()])
        ->assertActionVisible('refresh_evidence')
        ->assertActionDisabled('refresh_evidence')
        ->assertActionVisible('expire_snapshot')
        ->assertActionDisabled('expire_snapshot');
});

it('keeps evidence list actions within the declared action surface contract', function (): void {
    $tenant = 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' => ['finding_count' => 2, 'missing_dimensions' => 0],
        'generated_at' => now(),
    ]);

    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $livewire = Livewire::actingAs($user)
        ->test(ListEvidenceSnapshots::class)
        ->assertCanSeeTableRecords([$snapshot]);

    $table = $livewire->instance()->getTable();
    $rowActions = $table->getActions();

    $primaryRowActionNames = collect($rowActions)
        ->reject(static fn ($action): bool => $action instanceof ActionGroup)
        ->map(static fn ($action): ?string => $action->getName())
        ->filter()
        ->values()
        ->all();

    $moreGroup = collect($rowActions)->first(static fn ($action): bool => $action instanceof ActionGroup);
    $moreActionNames = collect($moreGroup?->getActions())
        ->map(static fn ($action): ?string => $action->getName())
        ->filter()
        ->values()
        ->all();

    expect($primaryRowActionNames)->toBe([])
        ->and($moreGroup)->toBeInstanceOf(ActionGroup::class)
        ->and($moreGroup?->getLabel())->toBe('More')
        ->and($moreActionNames)->toEqualCanonicalizing(['expire'])
        ->and($table->getBulkActions())->toBeEmpty()
        ->and($table->getRecordUrl($snapshot))->toBe(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot]));
});
