<?php

declare(strict_types=1);

use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentOnboardingSession;
use App\Models\User;
use App\Models\WorkspaceMembership;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\ProductKnowledge\ContextualHelpCatalog;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Verification\VerificationReportWriter;
use App\Support\Workspaces\WorkspaceContext;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

/**
 * @return array{0: User, 1: ManagedEnvironment, 2: ManagedEnvironmentOnboardingSession}
 */
function createProductKnowledgeOnboardingDraft(string $state, string $workspaceRole = 'owner', string $tenantRole = 'owner'): array
{
    $tenant = ManagedEnvironment::factory()->onboarding()->create();

    [$user, $tenant] = createUserWithTenant(
        tenant: $tenant,
        role: $tenantRole,
        workspaceRole: $workspaceRole,
        ensureDefaultMicrosoftProviderConnection: false,
    );

    $workspace = $tenant->workspace()->firstOrFail();

    $verificationConnection = ProviderConnection::factory()->platform()->consentGranted()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'managed_environment_id' => (int) $tenant->getKey(),
        'provider' => 'microsoft',
        'entra_tenant_id' => (string) $tenant->managed_environment_id,
        'display_name' => 'Verification connection',
        'is_default' => true,
        'consent_status' => 'granted',
    ]);

    $selectedConnection = $verificationConnection;
    $checks = [];
    $outcome = OperationRunOutcome::Blocked->value;

    if ($state === 'admin_consent') {
        $checks[] = [
            'key' => 'permissions.admin_consent',
            'title' => 'Admin consent',
            'status' => 'fail',
            'severity' => 'critical',
            'blocking' => true,
            'reason_code' => ProviderReasonCodes::ProviderConsentMissing,
            'message' => 'Admin consent is required before verification can proceed.',
            'evidence' => [],
            'next_steps' => [],
        ];
    } elseif ($state === 'required_permissions') {
        $checks[] = [
            'key' => 'permissions.required',
            'title' => 'Required application permissions',
            'status' => 'fail',
            'severity' => 'critical',
            'blocking' => true,
            'reason_code' => ProviderReasonCodes::ProviderPermissionMissing,
            'message' => 'Missing required application permissions.',
            'evidence' => [],
            'next_steps' => [],
        ];
    } elseif ($state === 'connection_unhealthy') {
        $checks[] = [
            'key' => 'provider.connection.check',
            'title' => 'Provider connection check',
            'status' => 'fail',
            'severity' => 'critical',
            'blocking' => true,
            'reason_code' => ProviderReasonCodes::ProviderAuthFailed,
            'message' => 'Stored provider credentials are no longer valid.',
            'evidence' => [],
            'next_steps' => [],
        ];
    } elseif ($state === 'verification_stale') {
        $selectedConnection = ProviderConnection::factory()->platform()->consentGranted()->create([
            'workspace_id' => (int) $workspace->getKey(),
            'managed_environment_id' => (int) $tenant->getKey(),
            'provider' => 'dummy',
            'entra_tenant_id' => (string) $tenant->managed_environment_id,
            'display_name' => 'Currently selected connection',
            'is_default' => false,
            'consent_status' => 'granted',
        ]);

        $checks[] = [
            'key' => 'provider.connection.check',
            'title' => 'Provider connection check',
            'status' => 'pass',
            'severity' => 'info',
            'blocking' => false,
            'reason_code' => 'ok',
            'message' => 'Connection is healthy.',
            'evidence' => [],
            'next_steps' => [],
        ];

        $outcome = OperationRunOutcome::Succeeded->value;
    } elseif ($state === 'verification_failed') {
        $checks[] = [
            'key' => 'provider.connection.check',
            'title' => 'Provider connection check',
            'status' => 'fail',
            'severity' => 'critical',
            'blocking' => true,
            'reason_code' => '',
            'message' => 'Verification failed after the prerequisite checks ran.',
            'evidence' => [],
            'next_steps' => [],
        ];

        $outcome = OperationRunOutcome::Failed->value;
    }

    $run = OperationRun::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'managed_environment_id' => (int) $tenant->getKey(),
        'type' => 'provider.connection.check',
        'status' => OperationRunStatus::Completed->value,
        'outcome' => $outcome,
        'context' => [
            'provider_connection_id' => (int) $verificationConnection->getKey(),
            'target_scope' => [
                'entra_tenant_id' => (string) $tenant->managed_environment_id,
                'entra_tenant_name' => (string) $tenant->name,
            ],
            'verification_report' => VerificationReportWriter::build('provider.connection.check', $checks),
        ],
    ]);

    $draft = createOnboardingDraft([
        'workspace' => $workspace,
        'tenant' => $tenant,
        'started_by' => $user,
        'updated_by' => $user,
        'current_step' => 'verify',
        'entra_tenant_id' => (string) $tenant->managed_environment_id,
        'state' => [
            'entra_tenant_id' => (string) $tenant->managed_environment_id,
            'environment_name' => (string) $tenant->name,
            'provider_connection_id' => (int) $selectedConnection->getKey(),
            'verification_operation_run_id' => (int) $run->getKey(),
        ],
    ]);

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

    return [$user, $tenant, $draft];
}

it('renders onboarding contextual help for each in-scope verification topic', function (
    string $state,
    string $headline,
    string $safeNextAction,
    ?string $linkLabel,
): void {
    [$user, , $draft] = createProductKnowledgeOnboardingDraft($state);

    $response = $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $user->last_workspace_id])
        ->followingRedirects()
        ->get(route('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()]));

    $response->assertSuccessful()
        ->assertSee('Verification report')
        ->assertSee('Stored verification details')
        ->assertSee($headline)
        ->assertDontSee('Permission diagnostics')
        ->assertSee($safeNextAction);

    $dom = new \DOMDocument();
    @$dom->loadHTML($response->getContent());

    $xpath = new \DOMXPath($dom);

    $headlineNodes = $xpath->query(sprintf(
        '//*[@data-testid="contextual-help-block"]//*[normalize-space(text())="%s"]',
        $headline,
    ));

    $storedVerificationDetailsHeadings = $xpath->query(
        '//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][normalize-space(text())="Stored verification details"]',
    );

    $verificationReportHeadings = $xpath->query(
        '//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6][normalize-space(text())="Verification report"]',
    );

    expect($headlineNodes?->length)->toBe(1);
    expect($storedVerificationDetailsHeadings?->length)->toBe(1);
    expect($verificationReportHeadings?->length)->toBeLessThanOrEqual(1);

    if ($state === 'admin_consent') {
        $primaryNextActionNode = $xpath->query(
            '//*[normalize-space(text())="Primary next action"]/following::*[(self::a or self::button) and normalize-space(text())!=""][1]',
        );

        expect(trim((string) $primaryNextActionNode?->item(0)?->textContent))->toContain('Grant admin consent');
    }

    if ($linkLabel !== null) {
        $response->assertSee($linkLabel);
    }
})->with([
    'admin consent required' => [
        'admin_consent',
        'Admin consent required',
        'Grant admin consent and re-run verification.',
        'Grant admin consent',
    ],
    'required permissions missing' => [
        'required_permissions',
        'Required permissions missing',
        'Open required permissions and confirm the missing grants.',
        'Open required permissions',
    ],
    'connection unhealthy' => [
        'connection_unhealthy',
        'Provider connection needs review',
        'Review the provider connection before retrying.',
        null,
    ],
    'verification stale' => [
        'verification_stale',
        'Verification result is stale',
        'Refresh verification before continuing onboarding.',
        null,
    ],
    'verification failed' => [
        'verification_failed',
        'Verification failed',
        'Review the blocking reason and retry verification.',
        null,
    ],
]);

it('keeps onboarding contextual help deny-as-not-found for workspace members outside the tenant scope', function (): void {
    [$authorizedUser, $tenant, $draft] = createProductKnowledgeOnboardingDraft('admin_consent');

    $workspace = $tenant->workspace()->firstOrFail();
    $outOfScopeUser = User::factory()->create();

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

    $outOfScopeUser->tenants()->syncWithoutDetaching([
        $allowedTenant->getKey() => ['role' => 'owner'],
    ]);
    app(\App\Services\Auth\CapabilityResolver::class)->clearCache();

    $this->actingAs($outOfScopeUser)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get(route('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()]))
        ->assertNotFound();
});
