<?php

declare(strict_types=1);

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

uses(RefreshDatabase::class);

function findingBulkAuditResourceIds(int $tenantId, string $action): array
{
    return AuditLog::query()
        ->where('managed_environment_id', $tenantId)
        ->where('action', $action)
        ->pluck('resource_id')
        ->map(static fn (string $resourceId): int => (int) $resourceId)
        ->sort()
        ->values()
        ->all();
}

it('supports bulk workflow actions and audits each record', function (): void {
    [$manager, $tenant] = createUserWithTenant(role: 'manager');
    $this->actingAs($manager);
    Filament::setTenant($tenant, true);

    $findings = Finding::factory()
        ->count(6)
        ->for($tenant)
        ->create([
            'status' => Finding::STATUS_NEW,
            'triaged_at' => null,
        ]);

    $component = Livewire::test(ListFindings::class);

    $component
        ->callTableBulkAction('triage_selected', $findings)
        ->assertHasNoTableBulkActionErrors();

    $findings->each(fn (Finding $finding) => expect($finding->refresh()->status)->toBe(Finding::STATUS_TRIAGED));

    expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.triaged'))
        ->toEqual($findings->pluck('id')->sort()->values()->all());

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

    $assignFindings = Finding::factory()
        ->count(3)
        ->for($tenant)
        ->create([
            'status' => Finding::STATUS_TRIAGED,
            'assignee_user_id' => null,
            'owner_user_id' => null,
        ]);

    $component
        ->callTableBulkAction('assign_selected', $assignFindings, data: [
            'assignee_user_id' => (int) $assignee->getKey(),
            'owner_user_id' => (int) $manager->getKey(),
        ])
        ->assertHasNoTableBulkActionErrors();

    $assignFindings->each(function (Finding $finding) use ($assignee, $manager): void {
        $finding->refresh();

        expect((int) $finding->assignee_user_id)->toBe((int) $assignee->getKey())
            ->and((int) $finding->owner_user_id)->toBe((int) $manager->getKey());
    });

    expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.assigned'))
        ->toEqual($assignFindings->pluck('id')->sort()->values()->all());

    $resolveFindings = Finding::factory()
        ->count(2)
        ->for($tenant)
        ->create([
            'status' => Finding::STATUS_IN_PROGRESS,
            'resolved_at' => null,
            'resolved_reason' => null,
        ]);

    $component
        ->callTableBulkAction('resolve_selected', $resolveFindings, data: [
            'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
        ])
        ->assertHasNoTableBulkActionErrors();

    $resolveFindings->each(function (Finding $finding): void {
        $finding->refresh();

        expect($finding->status)->toBe(Finding::STATUS_RESOLVED)
            ->and($finding->resolved_reason)->toBe(Finding::RESOLVE_REASON_REMEDIATED)
            ->and($finding->resolved_at)->not->toBeNull();
    });

    expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.resolved'))
        ->toEqual($resolveFindings->pluck('id')->sort()->values()->all());

    $closeFindings = Finding::factory()
        ->count(2)
        ->for($tenant)
        ->create([
            'status' => Finding::STATUS_TRIAGED,
            'closed_at' => null,
            'closed_reason' => null,
        ]);

    $component
        ->callTableBulkAction('close_selected', $closeFindings, data: [
            'closed_reason' => Finding::CLOSE_REASON_NO_LONGER_APPLICABLE,
        ])
        ->assertHasNoTableBulkActionErrors();

    $closeFindings->each(function (Finding $finding): void {
        $finding->refresh();

        expect($finding->status)->toBe(Finding::STATUS_CLOSED)
            ->and($finding->closed_reason)->toBe(Finding::CLOSE_REASON_NO_LONGER_APPLICABLE)
            ->and($finding->closed_at)->not->toBeNull()
            ->and($finding->closed_by_user_id)->not->toBeNull();
    });

    expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.closed'))
        ->toEqual($closeFindings->pluck('id')->sort()->values()->all());
});
