<?php

declare(strict_types=1);

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

uses(RefreshDatabase::class);

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 {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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 high severity quick filter', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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 {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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 {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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 {
    [$user, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($user);
    Filament::setTenant($tenant, true);

    $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());
});
