<?php

declare(strict_types=1);

use App\Filament\Pages\Findings\FindingsIntakeQueue;
use App\Filament\Resources\FindingResource;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Livewire\Livewire;

function findingsIntakeActingUser(string $role = 'owner', string $workspaceRole = 'readonly'): array
{
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: $role, workspaceRole: $workspaceRole);

    test()->actingAs($user);
    setAdminPanelContext();
    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

    return [$user, $tenant];
}

function findingsIntakePage(?User $user = null, array $query = [])
{
    if ($user instanceof User) {
        test()->actingAs($user);
    }

    setAdminPanelContext();

    $factory = $query === []
        ? Livewire::actingAs(auth()->user())
        : Livewire::withQueryParams($query)->actingAs(auth()->user());

    return $factory->test(FindingsIntakeQueue::class);
}

function makeIntakeFinding(ManagedEnvironment $tenant, array $attributes = []): Finding
{
    return Finding::factory()->for($tenant)->create(array_merge([
        'workspace_id' => (int) $tenant->workspace_id,
        'status' => Finding::STATUS_TRIAGED,
        'assignee_user_id' => null,
        'owner_user_id' => null,
        'subject_external_id' => fake()->uuid(),
    ], $attributes));
}

it('shows only visible unassigned open findings and exposes fixed queue view counts', function (): void {
    [$user, $tenantA] = findingsIntakeActingUser();
    $tenantA->forceFill(['name' => 'Alpha ManagedEnvironment'])->save();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'ManagedEnvironment Bravo',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

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

    $otherAssignee = User::factory()->create();
    createUserWithTenant($tenantA, $otherAssignee, role: 'operator', workspaceRole: 'readonly');

    $otherOwner = User::factory()->create();
    createUserWithTenant($tenantB, $otherOwner, role: 'owner', workspaceRole: 'readonly');

    $visibleNew = makeIntakeFinding($tenantA, [
        'subject_external_id' => 'visible-new',
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_NEW,
    ]);

    $visibleReopened = makeIntakeFinding($tenantB, [
        'subject_external_id' => 'visible-reopened',
        'status' => Finding::STATUS_REOPENED,
        'reopened_at' => now()->subHours(6),
        'owner_user_id' => (int) $otherOwner->getKey(),
    ]);

    $visibleTriaged = makeIntakeFinding($tenantA, [
        'subject_external_id' => 'visible-triaged',
        'status' => Finding::STATUS_TRIAGED,
    ]);

    $visibleInProgress = makeIntakeFinding($tenantB, [
        'subject_external_id' => 'visible-progress',
        'status' => Finding::STATUS_IN_PROGRESS,
        'due_at' => now()->subDay(),
    ]);

    $assignedOpen = makeIntakeFinding($tenantA, [
        'subject_external_id' => 'assigned-open',
        'assignee_user_id' => (int) $otherAssignee->getKey(),
    ]);

    $acknowledged = Finding::factory()->for($tenantA)->create([
        'workspace_id' => (int) $tenantA->workspace_id,
        'status' => 'acknowledged',
        'acknowledged_at' => now(),
        'assignee_user_id' => null,
        'subject_external_id' => 'acknowledged',
    ]);

    $terminal = Finding::factory()->for($tenantA)->resolved()->create([
        'workspace_id' => (int) $tenantA->workspace_id,
        'assignee_user_id' => null,
        'subject_external_id' => 'terminal',
    ]);

    $hidden = makeIntakeFinding($hiddenTenant, [
        'subject_external_id' => 'hidden-intake',
    ]);

    $component = findingsIntakePage($user)
        ->assertCanSeeTableRecords([$visibleNew, $visibleReopened, $visibleTriaged, $visibleInProgress])
        ->assertCanNotSeeTableRecords([$assignedOpen, $acknowledged, $terminal, $hidden])
        ->assertSee('Owner: '.$otherOwner->name)
        ->assertSee('Needs triage')
        ->assertSee('Unassigned');

    expect($component->instance()->summaryCounts())->toBe([
        'visible_unassigned' => 4,
        'visible_needs_triage' => 2,
        'visible_overdue' => 1,
    ]);

    $queueViews = collect($component->instance()->queueViews())->keyBy('key');

    expect($queueViews['unassigned']['badge_count'])->toBe(4)
        ->and($queueViews['unassigned']['active'])->toBeTrue()
        ->and($queueViews['needs_triage']['badge_count'])->toBe(2)
        ->and($queueViews['needs_triage']['active'])->toBeFalse();
});

it('defaults to the active tenant prefilter and lets the operator clear it without dropping intake scope', function (): void {
    [$user, $tenantA] = findingsIntakeActingUser();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Beta ManagedEnvironment',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    $findingA = makeIntakeFinding($tenantA, [
        'subject_external_id' => 'tenant-a',
        'status' => Finding::STATUS_NEW,
    ]);
    $findingB = makeIntakeFinding($tenantB, [
        'subject_external_id' => 'tenant-b',
        'status' => Finding::STATUS_TRIAGED,
    ]);

    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantB->getKey(),
    ]);

    $component = findingsIntakePage($user)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $tenantB->getKey())
        ->assertCanSeeTableRecords([$findingB])
        ->assertCanNotSeeTableRecords([$findingA])
        ->assertActionVisible('clear_tenant_filter');

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'fixed_scope' => 'visible_unassigned_open_findings_only',
        'queue_view' => 'unassigned',
        'queue_view_label' => 'Unassigned',
        'tenant_prefilter_source' => 'active_tenant_context',
        'tenant_label' => $tenantB->name,
    ]);

    $component->callAction('clear_tenant_filter')
        ->assertCanSeeTableRecords([$findingA, $findingB]);

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'fixed_scope' => 'visible_unassigned_open_findings_only',
        'queue_view' => 'unassigned',
        'queue_view_label' => 'Unassigned',
        'tenant_prefilter_source' => 'none',
        'tenant_label' => null,
    ]);
});

it('keeps the needs triage view active when clearing the tenant prefilter', function (): void {
    [$user, $tenantA] = findingsIntakeActingUser();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Beta ManagedEnvironment',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    $tenantATriage = makeIntakeFinding($tenantA, [
        'subject_external_id' => 'tenant-a-triage',
        'status' => Finding::STATUS_NEW,
    ]);
    $tenantBTriage = makeIntakeFinding($tenantB, [
        'subject_external_id' => 'tenant-b-triage',
        'status' => Finding::STATUS_REOPENED,
        'reopened_at' => now()->subHour(),
    ]);
    $tenantBBacklog = makeIntakeFinding($tenantB, [
        'subject_external_id' => 'tenant-b-backlog',
        'status' => Finding::STATUS_TRIAGED,
    ]);

    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantB->getKey(),
    ]);

    $component = findingsIntakePage($user, ['view' => 'needs_triage'])
        ->assertSet('tableFilters.managed_environment_id.value', (string) $tenantB->getKey())
        ->assertCanSeeTableRecords([$tenantBTriage])
        ->assertCanNotSeeTableRecords([$tenantATriage, $tenantBBacklog]);

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'fixed_scope' => 'visible_unassigned_open_findings_only',
        'queue_view' => 'needs_triage',
        'queue_view_label' => 'Needs triage',
        'tenant_prefilter_source' => 'active_tenant_context',
        'tenant_label' => $tenantB->name,
    ]);

    $component->callAction('clear_tenant_filter')
        ->assertCanSeeTableRecords([$tenantBTriage, $tenantATriage], inOrder: true)
        ->assertCanNotSeeTableRecords([$tenantBBacklog]);

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'fixed_scope' => 'visible_unassigned_open_findings_only',
        'queue_view' => 'needs_triage',
        'queue_view_label' => 'Needs triage',
        'tenant_prefilter_source' => 'none',
        'tenant_label' => null,
    ]);

    $queueViews = collect($component->instance()->queueViews())->keyBy('key');

    expect($queueViews['unassigned']['active'])->toBeFalse()
        ->and($queueViews['needs_triage']['active'])->toBeTrue();
});

it('separates needs triage from the remaining backlog and keeps deterministic urgency ordering', function (): void {
    [$user, $tenant] = findingsIntakeActingUser();

    $overdue = makeIntakeFinding($tenant, [
        'subject_external_id' => 'overdue',
        'status' => Finding::STATUS_TRIAGED,
        'due_at' => now()->subDay(),
    ]);

    $reopened = makeIntakeFinding($tenant, [
        'subject_external_id' => 'reopened',
        'status' => Finding::STATUS_REOPENED,
        'reopened_at' => now()->subHours(2),
        'due_at' => now()->addDay(),
    ]);

    $newFinding = makeIntakeFinding($tenant, [
        'subject_external_id' => 'new-finding',
        'status' => Finding::STATUS_NEW,
        'due_at' => now()->addDays(2),
    ]);

    $remainingBacklog = makeIntakeFinding($tenant, [
        'subject_external_id' => 'remaining-backlog',
        'status' => Finding::STATUS_TRIAGED,
        'due_at' => now()->addHours(12),
    ]);

    $undatedBacklog = makeIntakeFinding($tenant, [
        'subject_external_id' => 'undated-backlog',
        'status' => Finding::STATUS_IN_PROGRESS,
        'due_at' => null,
    ]);

    findingsIntakePage($user)
        ->assertCanSeeTableRecords([$overdue, $reopened, $newFinding, $remainingBacklog, $undatedBacklog], inOrder: true);

    findingsIntakePage($user, ['view' => 'needs_triage'])
        ->assertCanSeeTableRecords([$reopened, $newFinding], inOrder: true)
        ->assertCanNotSeeTableRecords([$overdue, $remainingBacklog, $undatedBacklog]);
});

it('builds tenant detail drilldowns with intake continuity', function (): void {
    [$user, $tenant] = findingsIntakeActingUser();

    $finding = makeIntakeFinding($tenant, [
        'subject_external_id' => 'continuity',
        'status' => Finding::STATUS_NEW,
    ]);

    $component = findingsIntakePage($user, [
        'tenant' => (string) $tenant->external_id,
        'view' => 'needs_triage',
    ]);

    $detailUrl = $component->instance()->getTable()->getRecordUrl($finding);

    expect($detailUrl)->toContain(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
        ->and($detailUrl)->toContain('nav%5Bback_label%5D=Back+to+findings+intake');

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get($detailUrl)
        ->assertOk()
        ->assertSee('Back to findings intake');
});

it('renders both intake empty-state branches with the correct single recovery action', function (): void {
    [$user, $tenantA] = findingsIntakeActingUser();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Work ManagedEnvironment',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    makeIntakeFinding($tenantB, [
        'subject_external_id' => 'available-elsewhere',
    ]);

    findingsIntakePage($user, [
        'tenant' => (string) $tenantA->external_id,
    ])
        ->assertSee('No intake findings match this environment scope')
        ->assertTableEmptyStateActionsExistInOrder(['clear_tenant_filter_empty']);

    Finding::query()->delete();
    session()->forget(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY);
    Filament::setTenant(null, true);

    findingsIntakePage($user)
        ->assertSee('Shared intake is clear')
        ->assertTableEmptyStateActionsExistInOrder(['open_my_findings_empty']);
});
