<?php

declare(strict_types=1);

use App\Filament\Pages\Governance\DecisionRegister;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\FindingExceptionDecision;
use App\Models\Tenant;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;

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('returns 403 for workspace members with no visible decisions in the default unfiltered register', function (): void {
    $tenant = Tenant::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'))
        ->assertForbidden();
});

it('hides the decision register page when the default workspace register would resolve to 403', function (): void {
    $tenant = Tenant::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('/admin')
        ->assertOk();

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

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

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

    $hiddenTenant = Tenant::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').'?tenant_id='.(string) $hiddenTenant->getKey())
        ->assertNotFound();
});

it('allows readonly tenant members to open the decision register when visible decisions exist', function (): void {
    $tenant = Tenant::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 = Tenant::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('/admin')
        ->assertOk();

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

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

function decisionRegisterAuthException(
    Tenant $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,
        'tenant_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,
        'tenant_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']);
}