<?php

declare(strict_types=1);

use App\Filament\Resources\FindingResource\Pages\ListFindings;
use App\Models\Finding;
use App\Models\User;
use App\Support\Findings\FindingOutcomeSemantics;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

function actingAsFindingsManagerForFilters(): array
{
    [$user, $tenant] = createUserWithTenant(role: 'manager');

    test()->actingAs($user);
    Filament::setTenant($tenant, true);

    return [$user, $tenant];
}

function findingFilterIndicatorLabels($component): array
{
    return collect($component->instance()->getTable()->getFilterIndicators())
        ->map(fn ($indicator): string => (string) $indicator->getLabel())
        ->all();
}

it('filters findings by overdue quick filter using open statuses only', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $overdueOpen = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'due_at' => now()->subDay(),
    ]);

    $notOverdueOpen = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_TRIAGED,
        'due_at' => now()->addDay(),
    ]);

    $overdueTerminal = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'due_at' => now()->subDay(),
    ]);

    Livewire::test(ListFindings::class)
        ->filterTable('overdue', true)
        ->assertCanSeeTableRecords([$overdueOpen])
        ->assertCanNotSeeTableRecords([$notOverdueOpen, $overdueTerminal]);
});

it('filters findings by workflow family and governance validity', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();
    [$requester] = createUserWithTenant(tenant: $tenant, role: 'owner');
    $approver = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');

    $active = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
    ]);

    $healthyAccepted = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
    ]);

    \App\Models\FindingException::query()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'finding_id' => (int) $healthyAccepted->getKey(),
        'requested_by_user_id' => (int) $requester->getKey(),
        'owner_user_id' => (int) $requester->getKey(),
        'approved_by_user_id' => (int) $approver->getKey(),
        'status' => \App\Models\FindingException::STATUS_ACTIVE,
        'current_validity_state' => \App\Models\FindingException::VALIDITY_VALID,
        'request_reason' => 'Healthy governance',
        'approval_reason' => 'Approved',
        'requested_at' => now()->subDays(5),
        'approved_at' => now()->subDays(4),
        'effective_from' => now()->subDays(4),
        'review_due_at' => now()->addDays(7),
        'expires_at' => now()->addDays(14),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    $lapsedAccepted = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
    ]);

    $historical = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'resolved_at' => now()->subDay(),
        'resolved_reason' => Finding::RESOLVE_REASON_NO_LONGER_DRIFTING,
    ]);

    Livewire::test(ListFindings::class)
        ->filterTable('workflow_family', 'historical')
        ->assertCanSeeTableRecords([$historical])
        ->assertCanNotSeeTableRecords([$active, $healthyAccepted, $lapsedAccepted])
        ->removeTableFilters()
        ->filterTable('governance_validity', \App\Models\FindingException::VALIDITY_VALID)
        ->assertCanSeeTableRecords([$healthyAccepted])
        ->assertCanNotSeeTableRecords([$active, $lapsedAccepted, $historical])
        ->removeTableFilters()
        ->filterTable('governance_validity', \App\Models\FindingException::VALIDITY_MISSING_SUPPORT)
        ->assertCanSeeTableRecords([$lapsedAccepted])
        ->assertCanNotSeeTableRecords([$active, $healthyAccepted, $historical]);
});

it('filters findings by canonical terminal outcome and verification state', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $pendingVerification = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
    ]);

    $verifiedCleared = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'resolved_reason' => Finding::RESOLVE_REASON_NO_LONGER_DRIFTING,
    ]);

    $closedDuplicate = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_CLOSED,
        'closed_reason' => Finding::CLOSE_REASON_DUPLICATE,
    ]);

    $riskAccepted = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
        'closed_reason' => Finding::CLOSE_REASON_ACCEPTED_RISK,
    ]);

    $openFinding = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
    ]);

    Livewire::test(ListFindings::class)
        ->filterTable('terminal_outcome', FindingOutcomeSemantics::OUTCOME_RESOLVED_PENDING_VERIFICATION)
        ->assertCanSeeTableRecords([$pendingVerification])
        ->assertCanNotSeeTableRecords([$verifiedCleared, $closedDuplicate, $riskAccepted, $openFinding])
        ->removeTableFilters()
        ->filterTable('terminal_outcome', FindingOutcomeSemantics::OUTCOME_VERIFIED_CLEARED)
        ->assertCanSeeTableRecords([$verifiedCleared])
        ->assertCanNotSeeTableRecords([$pendingVerification, $closedDuplicate, $riskAccepted, $openFinding])
        ->removeTableFilters()
        ->filterTable('terminal_outcome', FindingOutcomeSemantics::OUTCOME_CLOSED_DUPLICATE)
        ->assertCanSeeTableRecords([$closedDuplicate])
        ->assertCanNotSeeTableRecords([$pendingVerification, $verifiedCleared, $riskAccepted, $openFinding])
        ->removeTableFilters()
        ->filterTable('terminal_outcome', FindingOutcomeSemantics::OUTCOME_RISK_ACCEPTED)
        ->assertCanSeeTableRecords([$riskAccepted])
        ->assertCanNotSeeTableRecords([$pendingVerification, $verifiedCleared, $closedDuplicate, $openFinding])
        ->removeTableFilters()
        ->filterTable('verification_state', FindingOutcomeSemantics::VERIFICATION_PENDING)
        ->assertCanSeeTableRecords([$pendingVerification])
        ->assertCanNotSeeTableRecords([$verifiedCleared, $closedDuplicate, $riskAccepted, $openFinding])
        ->removeTableFilters()
        ->filterTable('verification_state', FindingOutcomeSemantics::VERIFICATION_VERIFIED)
        ->assertCanSeeTableRecords([$verifiedCleared])
        ->assertCanNotSeeTableRecords([$pendingVerification, $closedDuplicate, $riskAccepted, $openFinding]);
});

it('filters findings by high severity quick filter', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $critical = Finding::factory()->for($tenant)->create([
        'severity' => Finding::SEVERITY_CRITICAL,
        'status' => Finding::STATUS_NEW,
    ]);

    $high = Finding::factory()->for($tenant)->create([
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_TRIAGED,
    ]);

    $medium = Finding::factory()->for($tenant)->create([
        'severity' => Finding::SEVERITY_MEDIUM,
        'status' => Finding::STATUS_IN_PROGRESS,
    ]);

    Livewire::test(ListFindings::class)
        ->filterTable('high_severity', true)
        ->assertCanSeeTableRecords([$critical, $high])
        ->assertCanNotSeeTableRecords([$medium]);
});

it('filters findings by my assigned quick filter', function (): void {
    [$user, $tenant] = actingAsFindingsManagerForFilters();

    $otherUser = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $otherUser, role: 'operator');

    $assignedToMe = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'assignee_user_id' => (int) $user->getKey(),
    ]);

    $assignedToOther = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_TRIAGED,
        'assignee_user_id' => (int) $otherUser->getKey(),
    ]);

    $unassigned = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_REOPENED,
        'assignee_user_id' => null,
    ]);

    Livewire::test(ListFindings::class)
        ->filterTable('my_assigned', true)
        ->assertCanSeeTableRecords([$assignedToMe])
        ->assertCanNotSeeTableRecords([$assignedToOther, $unassigned]);
});

it('persists findings search, sort, and filter state while keeping the resource pagination profile', function (): void {
    actingAsFindingsManagerForFilters();

    $component = Livewire::test(ListFindings::class)
        ->searchTable('drift')
        ->call('sortTable', 'created_at', 'asc')
        ->set('tableFilters.status.value', Finding::STATUS_NEW);

    $table = $component->instance()->getTable();

    expect($table->getPaginationPageOptions())->toBe(\App\Support\Filament\TablePaginationProfiles::resource());
    expect(session()->get($component->instance()->getTableSearchSessionKey()))->toBe('drift');
    expect(session()->get($component->instance()->getTableSortSessionKey()))->toBe('created_at:asc');

    Livewire::test(ListFindings::class)
        ->assertSet('tableSearch', 'drift')
        ->assertSet('tableSort', 'created_at:asc')
        ->assertSet('tableFilters.status.value', Finding::STATUS_NEW);
});

it('composes status and created date filters with active indicators', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $matching = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'created_at' => now()->subDay(),
    ]);

    $wrongStatus = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'created_at' => now()->subDay(),
    ]);

    $outsideWindow = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'created_at' => now()->subDays(10),
    ]);

    $component = Livewire::test(ListFindings::class)
        ->filterTable('status', Finding::STATUS_NEW)
        ->set('tableFilters.created_at.from', now()->subDays(2)->toDateString())
        ->set('tableFilters.created_at.until', now()->toDateString())
        ->assertCanSeeTableRecords([$matching])
        ->assertCanNotSeeTableRecords([$wrongStatus, $outsideWindow]);

    expect(findingFilterIndicatorLabels($component))
        ->toContain('Created from '.now()->subDays(2)->toFormattedDateString())
        ->toContain('Created until '.now()->toFormattedDateString());
});

it('clears findings filters back to the default open view', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $openFinding = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'created_at' => now()->subDays(7),
    ]);

    $recentResolved = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RESOLVED,
        'created_at' => now()->subDay(),
    ]);

    $component = Livewire::test(ListFindings::class)
        ->set('tableFilters.open.isActive', false)
        ->filterTable('status', Finding::STATUS_RESOLVED)
        ->set('tableFilters.created_at.from', now()->subDays(2)->toDateString())
        ->set('tableFilters.created_at.until', now()->toDateString())
        ->assertCanSeeTableRecords([$recentResolved])
        ->assertCanNotSeeTableRecords([$openFinding]);

    $component
        ->removeTableFilter('status')
        ->removeTableFilter('created_at', 'from')
        ->removeTableFilter('created_at', 'until')
        ->assertSet('tableFilters.status.value', null)
        ->assertSet('tableFilters.created_at.from', null)
        ->assertSet('tableFilters.created_at.until', null);

    expect(findingFilterIndicatorLabels($component))
        ->not->toContain('Created from '.now()->subDays(2)->toFormattedDateString())
        ->not->toContain('Created until '.now()->toFormattedDateString());
});

it('prefilters dashboard open-drift drill-throughs to the named findings subset', function (): void {
    [, $tenant] = actingAsFindingsManagerForFilters();

    $openDrift = Finding::factory()->for($tenant)->create([
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'status' => Finding::STATUS_TRIAGED,
    ]);

    $openPermission = Finding::factory()->permissionPosture()->for($tenant)->create([
        'status' => Finding::STATUS_TRIAGED,
        'severity' => Finding::SEVERITY_HIGH,
    ]);

    $resolvedDrift = Finding::factory()->for($tenant)->create([
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'status' => Finding::STATUS_RESOLVED,
    ]);

    Livewire::withQueryParams([
        'tab' => 'needs_action',
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
    ])
        ->test(ListFindings::class)
        ->assertSet('activeTab', 'needs_action')
        ->assertSet('tableFilters.finding_type.value', Finding::FINDING_TYPE_DRIFT)
        ->assertCanSeeTableRecords([$openDrift])
        ->assertCanNotSeeTableRecords([$openPermission, $resolvedDrift]);
});

it('prefilters dashboard high-severity drill-throughs without carrying unrelated finding filters', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $criticalDrift = Finding::factory()->for($tenant)->create([
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'status' => Finding::STATUS_REOPENED,
        'severity' => Finding::SEVERITY_CRITICAL,
    ]);

    $highPermission = Finding::factory()->permissionPosture()->for($tenant)->create([
        'status' => Finding::STATUS_TRIAGED,
        'severity' => Finding::SEVERITY_HIGH,
    ]);

    $mediumDrift = Finding::factory()->for($tenant)->create([
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'status' => Finding::STATUS_NEW,
        'severity' => Finding::SEVERITY_MEDIUM,
    ]);

    Livewire::withQueryParams([
        'tab' => 'needs_action',
        'high_severity' => '1',
    ])
        ->test(ListFindings::class)
        ->assertSet('activeTab', 'needs_action')
        ->assertSet('tableFilters.high_severity.isActive', true)
        ->assertCanSeeTableRecords([$criticalDrift, $highPermission])
        ->assertCanNotSeeTableRecords([$mediumDrift]);
});

it('prefilters dashboard governance drill-throughs to the requested accepted-risk validity slice', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    [$requester] = createUserWithTenant(tenant: $tenant, role: 'owner');
    $approver = User::factory()->create();
    createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');

    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $healthyAccepted = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
    ]);

    \App\Models\FindingException::query()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'managed_environment_id' => (int) $tenant->getKey(),
        'finding_id' => (int) $healthyAccepted->getKey(),
        'requested_by_user_id' => (int) $requester->getKey(),
        'owner_user_id' => (int) $requester->getKey(),
        'approved_by_user_id' => (int) $approver->getKey(),
        'status' => \App\Models\FindingException::STATUS_ACTIVE,
        'current_validity_state' => \App\Models\FindingException::VALIDITY_VALID,
        'request_reason' => 'Healthy governance',
        'approval_reason' => 'Approved',
        'requested_at' => now()->subDays(5),
        'approved_at' => now()->subDays(4),
        'effective_from' => now()->subDays(4),
        'review_due_at' => now()->addDays(7),
        'expires_at' => now()->addDays(14),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    $lapsedAccepted = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_RISK_ACCEPTED,
    ]);

    Livewire::withQueryParams([
        'tab' => 'risk_accepted',
        'governance_validity' => \App\Models\FindingException::VALIDITY_MISSING_SUPPORT,
    ])
        ->test(ListFindings::class)
        ->assertSet('activeTab', 'risk_accepted')
        ->assertSet('tableFilters.governance_validity.value', \App\Models\FindingException::VALIDITY_MISSING_SUPPORT)
        ->assertCanSeeTableRecords([$lapsedAccepted])
        ->assertCanNotSeeTableRecords([$healthyAccepted]);
});
