<?php

declare(strict_types=1);

use App\Filament\Pages\Governance\DecisionRegister;
use App\Models\EvidenceSnapshot;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\Auth\Capabilities;
use App\Support\Evidence\EvidenceCompletenessState;
use App\Support\Evidence\EvidenceSnapshotStatus;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Gate;

uses(RefreshDatabase::class);

afterEach(function (): void {
    Filament::setCurrentPanel(null);
});

it('redirects decision register visits without workspace context into the existing workspace chooser flow', function (): void {
    $user = User::factory()->create();

    $workspaceA = Workspace::factory()->create();
    $workspaceB = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspaceA->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspaceB->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    $this->actingAs($user)
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertRedirect('/admin/choose-workspace');
});

it('returns 404 for users outside the active workspace on the decision register route', function (): void {
    $user = User::factory()->create();
    $workspace = Workspace::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) Workspace::factory()->create()->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertNotFound();
});

it('opens the default unfiltered register for authorized workspace members with no visible decisions', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertOk()
        ->assertSee('No open decisions match this filter right now.');
});

it('registers the decision register page for authorized workspace members even when the register is empty', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
        ->assertOk();

    $response->assertSee(DecisionRegister::getUrl(panel: 'admin'), false);

    expect(DecisionRegister::canAccess())->toBeTrue();
});

it('returns 404 for explicit tenant filters outside the actor scope', function (): void {
    $visibleTenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $visibleTenant] = createUserWithTenant($visibleTenant, role: 'readonly', workspaceRole: 'readonly');

    $hiddenTenant = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $visibleTenant->workspace_id,
    ]);

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
        ->get(DecisionRegister::getUrl(panel: 'admin').'?environment_id='.(string) $hiddenTenant->getKey())
        ->assertNotFound();
});

it('allows readonly tenant members to open the decision register when visible decisions exist', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');

    decisionRegisterAuthException(
        tenant: $tenant,
        actor: $user,
        status: FindingException::STATUS_PENDING,
        validityState: FindingException::VALIDITY_MISSING_SUPPORT,
        decisionType: FindingExceptionDecision::TYPE_REQUESTED,
        decisionReason: 'Visible approval request',
    );

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertOk()
        ->assertSee('Decision register');
});

it('registers the decision register page once visible open decisions exist', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');

    decisionRegisterAuthException(
        tenant: $tenant,
        actor: $user,
        status: FindingException::STATUS_PENDING,
        validityState: FindingException::VALIDITY_MISSING_SUPPORT,
        decisionType: FindingExceptionDecision::TYPE_REQUESTED,
        decisionReason: 'Visible approval request',
    );

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
        ->assertOk();

    $response->assertSee(DecisionRegister::getUrl(panel: 'admin'));

    expect(DecisionRegister::canAccess())->toBeTrue();
});

it('registers the decision register page and redirects the default route when only recently closed decisions are visible', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'readonly', workspaceRole: 'readonly');

    decisionRegisterAuthException(
        tenant: $tenant,
        actor: $user,
        status: FindingException::STATUS_REJECTED,
        validityState: FindingException::VALIDITY_REJECTED,
        decisionType: FindingExceptionDecision::TYPE_REJECTED,
        decisionReason: 'Recently rejected closure reason',
    );

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(route('admin.workspace.home', ['workspace' => $tenant->workspace]))
        ->assertOk();

    $response->assertSee(DecisionRegister::getUrl(panel: 'admin'));

    expect(DecisionRegister::canAccess())->toBeTrue();

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertRedirect(DecisionRegister::getUrl(panel: 'admin', parameters: ['register_state' => 'recently_closed']));
});

it('does not render direct evidence links when the actor lacks evidence destination access', function (): void {
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: 'owner', workspaceRole: 'owner');

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

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

    $exception = decisionRegisterAuthException(
        tenant: $tenant,
        actor: $user,
        status: FindingException::STATUS_PENDING,
        validityState: FindingException::VALIDITY_MISSING_SUPPORT,
        decisionType: FindingExceptionDecision::TYPE_REQUESTED,
        decisionReason: 'Evidence access denied request',
    );

    $exception->forceFill(['evidence_summary' => ['reference_count' => 1]])->save();
    $exception->evidenceReferences()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'source_type' => 'evidence_snapshot',
        'source_id' => (string) $snapshot->getKey(),
        'label' => 'Evidence snapshot',
        'summary_payload' => [],
    ]);

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get(DecisionRegister::getUrl(panel: 'admin'))
        ->assertOk()
        ->assertSee('1 proof item')
        ->assertSee('View proof')
        ->assertDontSee('View evidence')
        ->assertDontSee('/admin/t', false);
});

function decisionRegisterAuthException(
    ManagedEnvironment $tenant,
    User $actor,
    string $status,
    string $validityState,
    string $decisionType,
    string $decisionReason,
): FindingException {
    $finding = Finding::factory()->for($tenant)->create([
        'workspace_id' => (int) $tenant->workspace_id,
    ]);

    $exception = FindingException::query()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'finding_id' => (int) $finding->getKey(),
        'requested_by_user_id' => (int) $actor->getKey(),
        'owner_user_id' => (int) $actor->getKey(),
        'status' => $status,
        'current_validity_state' => $validityState,
        'request_reason' => 'Decision register authorization test',
        'requested_at' => now()->subDay(),
        'review_due_at' => now()->addDay(),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    $decision = $exception->decisions()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'actor_user_id' => (int) $actor->getKey(),
        'decision_type' => $decisionType,
        'reason' => $decisionReason,
        'metadata' => [],
        'decided_at' => now()->subDay(),
    ]);

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

    return $exception->fresh(['currentDecision']);
}
