<?php

declare(strict_types=1);

use App\Filament\Pages\Workspaces\ManagedTenantOnboardingWizard;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\Tenant;
use App\Models\TenantOnboardingSession;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Support\OperationRunLinks;
use App\Support\Verification\VerificationReportSchema;
use App\Support\Verification\VerificationReportWriter;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Support\Facades\Queue;
use Livewire\Livewire;

it('dedupes active verification runs and stores the run id in the onboarding session', function (): void {
    Queue::fake();

    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());

    $entraTenantId = '77777777-7777-7777-7777-777777777777';

    $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class);

    $component->call('identifyManagedTenant', [
        'entra_tenant_id' => $entraTenantId,
        'environment' => 'prod',
        'name' => 'Acme',
    ]);

    $component->call('createProviderConnection', [
        'display_name' => 'Acme connection',
        'client_id' => '00000000-0000-0000-0000-000000000000',
        'client_secret' => 'super-secret',
        'is_default' => true,
    ]);

    $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail();

    ProviderConnection::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->update(['status' => 'connected']);

    $component->call('startVerification');
    $component->call('startVerification');

    expect(OperationRun::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('type', 'provider.connection.check')
        ->count())->toBe(1);

    $runId = (int) OperationRun::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('type', 'provider.connection.check')
        ->value('id');

    $notificationActionUrls = collect(session('filament.notifications', []))
        ->flatMap(static fn (array $notification): array => is_array($notification['actions'] ?? null)
            ? $notification['actions']
            : [])
        ->pluck('url')
        ->filter(static fn (mixed $url): bool => is_string($url) && trim($url) !== '')
        ->values()
        ->all();

    expect($notificationActionUrls)->toContain(OperationRunLinks::tenantlessView($runId));

    $session = TenantOnboardingSession::query()
        ->where('workspace_id', (int) $workspace->getKey())
        ->where('entra_tenant_id', $entraTenantId)
        ->whereNull('completed_at')
        ->firstOrFail();

    expect($session->state['verification_operation_run_id'] ?? null)->toBe($runId);
});

it('stores a blocked verification report and canonical link when onboarding verification cannot proceed', function (): void {
    Queue::fake();

    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());

    $entraTenantId = '72727272-7272-7272-7272-727272727272';

    $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class);

    $component->call('identifyManagedTenant', [
        'entra_tenant_id' => $entraTenantId,
        'environment' => 'prod',
        'name' => 'Blocked Tenant',
    ]);

    $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail();

    $connection = ProviderConnection::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'entra_tenant_id' => $entraTenantId,
        'display_name' => 'Blocked connection',
        'is_default' => true,
        'status' => 'connected',
    ]);

    $component->call('selectProviderConnection', (int) $connection->getKey());
    $component->call('startVerification');

    $run = OperationRun::query()
        ->where('tenant_id', (int) $tenant->getKey())
        ->where('type', 'provider.connection.check')
        ->latest('id')
        ->first();

    expect($run)->not->toBeNull()
        ->and($run?->outcome)->toBe('blocked');

    $report = $run?->context['verification_report'] ?? null;

    expect($report)->toBeArray();
    expect(VerificationReportSchema::isValidReport($report))->toBeTrue();

    $notificationActionUrls = collect(session('filament.notifications', []))
        ->flatMap(static fn (array $notification): array => is_array($notification['actions'] ?? null)
            ? $notification['actions']
            : [])
        ->pluck('url')
        ->filter(static fn (mixed $url): bool => is_string($url) && trim($url) !== '')
        ->values()
        ->all();

    expect($notificationActionUrls)->toContain(OperationRunLinks::tenantlessView((int) $run?->getKey()));

    Queue::assertNothingPushed();
});

it('renders stored verification findings in the wizard report section', function (): void {
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());

    $entraTenantId = '99999999-9999-9999-9999-999999999999';

    $tenant = Tenant::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => $entraTenantId,
        'status' => 'onboarding',
    ]);

    $run = OperationRun::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'type' => 'provider.connection.check',
        'status' => 'completed',
        'outcome' => 'failed',
        'context' => [
            'target_scope' => [
                'entra_tenant_id' => $entraTenantId,
                'entra_tenant_name' => 'Contoso',
            ],
            'verification_report' => VerificationReportWriter::build('provider.connection.check', [
                [
                    'key' => 'permission_check',
                    'title' => 'Graph permissions',
                    'status' => 'fail',
                    'severity' => 'high',
                    'blocking' => true,
                    'reason_code' => 'permission_denied',
                    'message' => 'Missing required Graph permissions.',
                    'evidence' => [],
                    'next_steps' => [],
                ],
            ]),
        ],
    ]);

    TenantOnboardingSession::query()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'entra_tenant_id' => $entraTenantId,
        'current_step' => 'verify',
        'state' => [
            'verification_operation_run_id' => (int) $run->getKey(),
        ],
        'started_by_user_id' => (int) $user->getKey(),
        'updated_by_user_id' => (int) $user->getKey(),
    ]);

    $this->actingAs($user)
        ->get('/admin/onboarding')
        ->assertSuccessful()
        ->assertSee('Missing required Graph permissions.')
        ->assertSee('Graph permissions')
        ->assertSee($entraTenantId);
});

it('clears the stored verification run id when switching provider connections', function (): void {
    Queue::fake();

    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());

    $entraTenantId = '12121212-1212-1212-1212-121212121212';

    $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class);

    $component->call('identifyManagedTenant', [
        'entra_tenant_id' => $entraTenantId,
        'environment' => 'prod',
        'name' => 'Acme',
    ]);

    $component->call('createProviderConnection', [
        'display_name' => 'Acme connection',
        'client_id' => '00000000-0000-0000-0000-000000000000',
        'client_secret' => 'super-secret',
        'is_default' => true,
    ]);

    $tenant = Tenant::query()->where('tenant_id', $entraTenantId)->firstOrFail();

    $otherConnection = ProviderConnection::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'provider' => 'dummy',
        'entra_tenant_id' => $entraTenantId,
        'display_name' => 'Dummy connection',
        'is_default' => false,
    ]);

    $component->call('startVerification');

    $session = TenantOnboardingSession::query()
        ->where('workspace_id', (int) $workspace->getKey())
        ->where('entra_tenant_id', $entraTenantId)
        ->whereNull('completed_at')
        ->firstOrFail();

    expect($session->state['verification_operation_run_id'] ?? null)->toBeInt();

    $component->call('selectProviderConnection', (int) $otherConnection->getKey());

    $session->refresh();

    expect($session->state['verification_operation_run_id'] ?? null)->toBeNull();
});

it('treats a completed verification run as stale when it belongs to a different provider connection', function (): void {
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'user_id' => (int) $user->getKey(),
        'role' => 'owner',
    ]);

    session()->put(WorkspaceContext::SESSION_KEY, (int) $workspace->getKey());

    $entraTenantId = '13131313-1313-1313-1313-131313131313';

    $tenant = Tenant::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => $entraTenantId,
        'status' => Tenant::STATUS_ONBOARDING,
    ]);

    $microsoftConnection = ProviderConnection::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'entra_tenant_id' => $entraTenantId,
        'display_name' => 'Microsoft connection',
        'is_default' => true,
    ]);

    $otherConnection = ProviderConnection::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'provider' => 'dummy',
        'entra_tenant_id' => $entraTenantId,
        'display_name' => 'Dummy connection',
        'is_default' => false,
    ]);

    $run = OperationRun::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'type' => 'provider.connection.check',
        'status' => 'completed',
        'outcome' => 'succeeded',
        'context' => [
            'provider_connection_id' => (int) $microsoftConnection->getKey(),
            'target_scope' => [
                'entra_tenant_id' => $entraTenantId,
            ],
        ],
    ]);

    TenantOnboardingSession::query()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'tenant_id' => (int) $tenant->getKey(),
        'entra_tenant_id' => $entraTenantId,
        'current_step' => 'verify',
        'state' => [
            'provider_connection_id' => (int) $otherConnection->getKey(),
            'verification_operation_run_id' => (int) $run->getKey(),
        ],
        'started_by_user_id' => (int) $user->getKey(),
        'updated_by_user_id' => (int) $user->getKey(),
    ]);

    $component = Livewire::actingAs($user)->test(ManagedTenantOnboardingWizard::class);

    expect($component->instance()->verificationSucceeded())->toBeFalse();
});
