<?php

declare(strict_types=1);

use App\Models\AuditLog;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Services\Verification\VerificationCheckAcknowledgementService;
use App\Support\Audit\AuditActionId;
use App\Support\Verification\VerificationReportFingerprint;
use Illuminate\Auth\Access\AuthorizationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

it('returns 404 for non-members on verification check acknowledgement', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    $otherTenant = ManagedEnvironment::factory()->create();

    [$user] = createUserWithTenant($otherTenant, role: 'readonly');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'provider.connection.check',
        'status' => 'completed',
        'outcome' => 'failed',
        'context' => [
            'verification_report' => json_decode(
                (string) file_get_contents(repo_path('specs/074-verification-checklist/contracts/examples/fail.json')),
                true,
                512,
                JSON_THROW_ON_ERROR,
            ),
        ],
    ]);

    expect(fn () => app(VerificationCheckAcknowledgementService::class)->acknowledge(
        tenant: $tenant,
        run: $run,
        checkKey: 'provider_connection.token_acquisition',
        ackReason: 'Known issue',
        expiresAt: null,
        actor: $user,
    ))->toThrow(NotFoundHttpException::class);
});

it('returns 403 for members without tenant_verification.acknowledge on verification check acknowledgement', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'operator');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'provider.connection.check',
        'status' => 'completed',
        'outcome' => 'failed',
        'context' => [
            'verification_report' => json_decode(
                (string) file_get_contents(repo_path('specs/074-verification-checklist/contracts/examples/fail.json')),
                true,
                512,
                JSON_THROW_ON_ERROR,
            ),
        ],
    ]);

    expect(fn () => app(VerificationCheckAcknowledgementService::class)->acknowledge(
        tenant: $tenant,
        run: $run,
        checkKey: 'provider_connection.token_acquisition',
        ackReason: 'Known issue',
        expiresAt: null,
        actor: $user,
    ))->toThrow(AuthorizationException::class);
});

it('acknowledges a failing check (with optional expiry) and writes a minimal audit log (no ack_reason)', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'manager');

    $run = OperationRun::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'type' => 'provider.connection.check',
        'status' => 'completed',
        'outcome' => 'failed',
        'context' => [
            'verification_report' => json_decode(
                (string) file_get_contents(repo_path('specs/074-verification-checklist/contracts/examples/fail.json')),
                true,
                512,
                JSON_THROW_ON_ERROR,
            ),
        ],
    ]);

    $reportBefore = $run->context['verification_report'] ?? null;
    $reportBefore = is_array($reportBefore) ? $reportBefore : [];

    $summaryBefore = is_array($reportBefore['summary'] ?? null) ? $reportBefore['summary'] : [];
    $countsBefore = is_array($summaryBefore['counts'] ?? null) ? $summaryBefore['counts'] : [];

    $checksBefore = is_array($reportBefore['checks'] ?? null) ? $reportBefore['checks'] : [];
    $checksBeforeByKey = collect($checksBefore)
        ->filter(fn ($check): bool => is_array($check) && is_string($check['key'] ?? null))
        ->mapWithKeys(fn (array $check): array => [
            (string) $check['key'] => [
                'status' => $check['status'] ?? null,
                'blocking' => $check['blocking'] ?? null,
                'severity' => $check['severity'] ?? null,
                'reason_code' => $check['reason_code'] ?? null,
            ],
        ])
        ->all();

    $fingerprintBefore = VerificationReportFingerprint::forReport($reportBefore);

    $expiresAt = now()->addDay()->toISOString();

    $ack = app(VerificationCheckAcknowledgementService::class)->acknowledge(
        tenant: $tenant,
        run: $run,
        checkKey: 'provider_connection.token_acquisition',
        ackReason: 'Known issue',
        expiresAt: $expiresAt,
        actor: $user,
    );

    expect($ack->operation_run_id)->toBe((int) $run->getKey());
    expect($ack->check_key)->toBe('provider_connection.token_acquisition');
    expect($ack->ack_reason)->toBe('Known issue');
    expect($ack->expires_at)->not->toBeNull();

    $run->refresh();

    $reportAfter = $run->context['verification_report'] ?? null;
    $reportAfter = is_array($reportAfter) ? $reportAfter : [];

    $summaryAfter = is_array($reportAfter['summary'] ?? null) ? $reportAfter['summary'] : [];
    $countsAfter = is_array($summaryAfter['counts'] ?? null) ? $summaryAfter['counts'] : [];

    expect($summaryAfter['overall'] ?? null)->toBe($summaryBefore['overall'] ?? null);
    expect($countsAfter)->toBe($countsBefore);

    $checksAfter = is_array($reportAfter['checks'] ?? null) ? $reportAfter['checks'] : [];
    $checksAfterByKey = collect($checksAfter)
        ->filter(fn ($check): bool => is_array($check) && is_string($check['key'] ?? null))
        ->mapWithKeys(fn (array $check): array => [
            (string) $check['key'] => [
                'status' => $check['status'] ?? null,
                'blocking' => $check['blocking'] ?? null,
                'severity' => $check['severity'] ?? null,
                'reason_code' => $check['reason_code'] ?? null,
            ],
        ])
        ->all();

    expect($checksAfterByKey)->toBe($checksBeforeByKey);

    $fingerprintAfter = VerificationReportFingerprint::forReport($reportAfter);
    expect($fingerprintAfter)->toBe($fingerprintBefore);

    $audit = AuditLog::query()
        ->where('workspace_id', (int) $tenant->workspace_id)
        ->where('action', AuditActionId::VerificationCheckAcknowledged->value)
        ->latest('id')
        ->first();

    expect($audit)->not->toBeNull();
    expect($audit?->metadata)->toMatchArray([
        'managed_environment_id' => (int) $tenant->getKey(),
        'operation_run_id' => (int) $run->getKey(),
        'report_id' => (int) $run->getKey(),
        'flow' => 'provider.connection.check',
        'check_key' => 'provider_connection.token_acquisition',
        'reason_code' => 'authentication_failed',
    ]);

    $metadata = $audit?->metadata ?? [];

    expect($metadata)->not->toHaveKey('ack_reason');
    expect(json_encode($metadata))->not->toContain('Known issue');
});
