<?php

declare(strict_types=1);

use App\Models\Finding;
use App\Models\User;
use App\Services\Findings\FindingWorkflowService;
use Illuminate\Auth\Access\AuthorizationException;

it('classifies assignment audit metadata for owner assignee and clear mutations', function (
    string $ownerTarget,
    string $assigneeTarget,
    string $expectedClassification,
    string $expectedSummary,
): void {
    [$actor, $tenant] = createUserWithTenant(role: 'owner');

    $existingOwner = User::factory()->create(['name' => 'Existing Owner']);
    createUserWithTenant(tenant: $tenant, user: $existingOwner, role: 'manager');

    $existingAssignee = User::factory()->create(['name' => 'Existing Assignee']);
    createUserWithTenant(tenant: $tenant, user: $existingAssignee, role: 'operator');

    $replacementOwner = User::factory()->create(['name' => 'Replacement Owner']);
    createUserWithTenant(tenant: $tenant, user: $replacementOwner, role: 'manager');

    $replacementAssignee = User::factory()->create(['name' => 'Replacement Assignee']);
    createUserWithTenant(tenant: $tenant, user: $replacementAssignee, role: 'operator');

    $finding = $this->makeFindingForWorkflow($tenant, Finding::STATUS_TRIAGED, [
        'owner_user_id' => (int) $existingOwner->getKey(),
        'assignee_user_id' => (int) $existingAssignee->getKey(),
    ]);

    $ownerUserId = match ($ownerTarget) {
        'replacement' => (int) $replacementOwner->getKey(),
        'clear' => null,
        default => (int) $existingOwner->getKey(),
    };

    $assigneeUserId = match ($assigneeTarget) {
        'replacement' => (int) $replacementAssignee->getKey(),
        'clear' => null,
        default => (int) $existingAssignee->getKey(),
    };

    $updated = app(FindingWorkflowService::class)->assign(
        finding: $finding,
        tenant: $tenant,
        actor: $actor,
        assigneeUserId: $assigneeUserId,
        ownerUserId: $ownerUserId,
    );

    $audit = $this->latestFindingAudit($updated, 'finding.assigned');

    expect($audit)->not->toBeNull();
    expect(data_get($audit?->metadata, 'responsibility_change_classification'))->toBe($expectedClassification)
        ->and(data_get($audit?->metadata, 'responsibility_change_summary'))->toBe($expectedSummary);

    expect($updated->owner_user_id)->toBe($ownerUserId)
        ->and($updated->assignee_user_id)->toBe($assigneeUserId);
})->with([
    'owner only' => ['replacement', 'existing', 'owner_only', 'Updated the accountable owner and kept the active assignee unchanged.'],
    'assignee only' => ['existing', 'replacement', 'assignee_only', 'Updated the active assignee and kept the accountable owner unchanged.'],
    'owner and assignee' => ['replacement', 'replacement', 'owner_and_assignee', 'Updated the accountable owner and the active assignee.'],
    'clear owner' => ['clear', 'existing', 'clear_owner', 'Cleared the accountable owner and kept the active assignee unchanged.'],
    'clear assignee' => ['existing', 'clear', 'clear_assignee', 'Cleared the active assignee and kept the accountable owner unchanged.'],
]);

it('preserves 403 semantics for in-scope members without assignment capability', function (): void {
    [$owner, $tenant] = createUserWithTenant(role: 'owner');
    [$readonly] = createUserWithTenant(tenant: $tenant, role: 'readonly');

    $finding = $this->makeFindingForWorkflow($tenant, Finding::STATUS_NEW);

    expect(fn () => app(FindingWorkflowService::class)->assign(
        finding: $finding,
        tenant: $tenant,
        actor: $readonly,
        assigneeUserId: null,
        ownerUserId: (int) $owner->getKey(),
    ))->toThrow(AuthorizationException::class);
});
