<?php

declare(strict_types=1);

use App\Models\Finding;
use App\Models\User;
use App\Services\Findings\FindingWorkflowService;
use Carbon\CarbonImmutable;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('enforces transition rules and required reasons', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');

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

    $service = app(FindingWorkflowService::class);

    expect(fn () => $service->startProgress($finding, $tenant, $user))
        ->toThrow(\InvalidArgumentException::class);

    expect(fn () => $service->resolve($finding, $tenant, $user, '   '))
        ->toThrow(\InvalidArgumentException::class);
});

it('resets due_at and sla_days when reopening and clears terminal fields', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');

    CarbonImmutable::setTestNow(CarbonImmutable::parse('2026-02-24T10:00:00Z'));

    $finding = Finding::factory()->for($tenant)->create([
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_RESOLVED,
        'resolved_at' => now()->subDay(),
        'resolved_reason' => Finding::RESOLVE_REASON_REMEDIATED,
        'closed_at' => now()->subHours(2),
        'closed_reason' => 'legacy-close',
        'closed_by_user_id' => $user->getKey(),
        'sla_days' => 30,
        'due_at' => now()->subDays(10),
    ]);

    $reopened = app(FindingWorkflowService::class)->reopen($finding, $tenant, $user, Finding::REOPEN_REASON_MANUAL_REASSESSMENT);

    expect($reopened->status)->toBe(Finding::STATUS_REOPENED)
        ->and($reopened->reopened_at?->toIso8601String())->toBe('2026-02-24T10:00:00+00:00')
        ->and($reopened->sla_days)->toBe(7)
        ->and($reopened->due_at?->toIso8601String())->toBe('2026-03-03T10:00:00+00:00')
        ->and($reopened->resolved_at)->toBeNull()
        ->and($reopened->resolved_reason)->toBeNull()
        ->and($reopened->closed_at)->toBeNull()
        ->and($reopened->closed_reason)->toBeNull()
        ->and($reopened->closed_by_user_id)->toBeNull();

    CarbonImmutable::setTestNow();
});

it('keeps due_at stable across open workflow transitions even if severity changes while open', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');

    $dueAt = now()->addDays(14)->startOfMinute();

    $finding = Finding::factory()->for($tenant)->create([
        'status' => Finding::STATUS_NEW,
        'severity' => Finding::SEVERITY_MEDIUM,
        'due_at' => $dueAt,
        'sla_days' => 14,
    ]);

    $finding->forceFill(['severity' => Finding::SEVERITY_CRITICAL])->save();

    $service = app(FindingWorkflowService::class);
    $service->triage($finding->refresh(), $tenant, $user);
    $inProgress = $service->startProgress($finding->refresh(), $tenant, $user);

    expect($inProgress->status)->toBe(Finding::STATUS_IN_PROGRESS)
        ->and($inProgress->due_at?->toIso8601String())->toBe($dueAt->toIso8601String())
        ->and($inProgress->sla_days)->toBe(14);
});

it('allows assigning current tenant members and rejects non-members', function (): void {
    [$manager, $tenant] = createUserWithTenant(role: 'manager');

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

    $outsider = User::factory()->create();

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

    $service = app(FindingWorkflowService::class);

    $assigned = $service->assign($finding, $tenant, $manager, (int) $assignee->getKey(), (int) $manager->getKey());

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

    expect(fn () => $service->assign($assigned, $tenant, $manager, (int) $outsider->getKey(), null))
        ->toThrow(\InvalidArgumentException::class);
});
