<?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);

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(101)
        ->for($tenant)
        ->create([
            'status' => Finding::STATUS_NEW,
            'triaged_at' => null,
        ]);

    Livewire::test(ListFindings::class)
        ->callTableBulkAction('triage_selected', $findings)
        ->assertHasNoTableBulkActionErrors();

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

    expect(AuditLog::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('action', 'finding.triaged')
        ->count())->toBe(101);

    $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,
        ]);

    Livewire::test(ListFindings::class)
        ->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(AuditLog::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('action', 'finding.assigned')
        ->count())->toBe(3);

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

    Livewire::test(ListFindings::class)
        ->callTableBulkAction('resolve_selected', $resolveFindings, data: [
            'resolved_reason' => 'fixed',
        ])
        ->assertHasNoTableBulkActionErrors();

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

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

    expect(AuditLog::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('action', 'finding.resolved')
        ->count())->toBe(2);

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

    Livewire::test(ListFindings::class)
        ->callTableBulkAction('close_selected', $closeFindings, data: [
            'closed_reason' => 'not applicable',
        ])
        ->assertHasNoTableBulkActionErrors();

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

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

    expect(AuditLog::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('action', 'finding.closed')
        ->count())->toBe(2);

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

    Livewire::test(ListFindings::class)
        ->callTableBulkAction('risk_accept_selected', $riskFindings, data: [
            'closed_reason' => 'accepted risk',
        ])
        ->assertHasNoTableBulkActionErrors();

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

        expect($finding->status)->toBe(Finding::STATUS_RISK_ACCEPTED)
            ->and($finding->closed_reason)->toBe('accepted risk')
            ->and($finding->closed_at)->not->toBeNull()
            ->and($finding->closed_by_user_id)->not->toBeNull();
    });

    expect(AuditLog::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('action', 'finding.risk_accepted')
        ->count())->toBe(2);
});
