<?php

declare(strict_types=1);

use App\Models\AuditLog;
use App\Models\OperationalControlActivation;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Models\WorkspaceMembership;
use App\Models\WorkspaceSetting;
use App\Support\Ai\AiDataClassification;
use App\Support\Ai\AiDecisionReasonCode;
use App\Support\Ai\AiExecutionRequest;
use App\Support\Ai\AiProviderClass;
use App\Support\Ai\GovernedAiExecutionBoundary;
use App\Support\Audit\AuditActionId;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

/**
 * @return array{0: Workspace, 1: User}
 */
function aiPolicyWorkspace(string $policyMode = 'private_only'): array
{
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

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

    WorkspaceSetting::query()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'domain' => 'ai',
        'key' => 'policy_mode',
        'value' => $policyMode,
        'updated_by_user_id' => (int) $user->getKey(),
    ]);

    return [$workspace, $user];
}

it('allows approved local-private support-diagnostics requests and writes bounded audit metadata', function (): void {
    [$workspace, $user] = aiPolicyWorkspace();
    $tenant = ManagedEnvironment::factory()->create(['workspace_id' => (int) $workspace->getKey()]);

    $decision = assertNoOutboundHttp(fn () => app(GovernedAiExecutionBoundary::class)->evaluate(new AiExecutionRequest(
        workspace: $workspace,
        tenant: $tenant,
        actor: $user,
        useCaseKey: 'support_diagnostics.summary_draft',
        requestedProviderClass: AiProviderClass::LocalPrivate->value,
        dataClassifications: [AiDataClassification::RedactedSupportSummary->value],
        sourceFamily: 'support_diagnostics',
        callerSurface: 'support_diagnostics',
        contextFingerprint: 'support_diagnostics:summary:v1',
    )));

    expect($decision->isAllowed())->toBeTrue()
        ->and($decision->reasonCode)->toBe(AiDecisionReasonCode::Allowed)
        ->and($decision->workspaceAiPolicyMode)->toBe('private_only')
        ->and($decision->matchedOperationalControlScope)->toBeNull();

    $audit = AuditLog::query()->latest('id')->first();

    expect($audit)->not->toBeNull()
        ->and($audit?->action)->toBe(AuditActionId::AiExecutionDecisionEvaluated->value)
        ->and($audit?->workspace_id)->toBe((int) $workspace->getKey())
        ->and($audit?->managed_environment_id)->toBe((int) $tenant->getKey())
        ->and(data_get($audit?->metadata, 'decision_outcome'))->toBe('allowed')
        ->and(data_get($audit?->metadata, 'decision_reason'))->toBe(AiDecisionReasonCode::Allowed->value)
        ->and(data_get($audit?->metadata, 'use_case_key'))->toBe('support_diagnostics.summary_draft')
        ->and(data_get($audit?->metadata, 'requested_provider_class'))->toBe('local_private')
        ->and(data_get($audit?->metadata, 'data_classifications'))->toBe(['redacted_support_summary'])
        ->and(data_get($audit?->metadata, 'context_fingerprint'))->toBe('support_diagnostics:summary:v1')
        ->and(data_get($audit?->metadata, 'prompt_text'))->toBeNull()
        ->and(data_get($audit?->metadata, 'output_text'))->toBeNull();
});

it('blocks external-public provider classes before any provider resolution', function (): void {
    [$workspace, $user] = aiPolicyWorkspace();

    $decision = assertNoOutboundHttp(fn () => app(GovernedAiExecutionBoundary::class)->evaluate(new AiExecutionRequest(
        workspace: $workspace,
        tenant: null,
        actor: $user,
        useCaseKey: 'product_knowledge.answer_draft',
        requestedProviderClass: AiProviderClass::ExternalPublic->value,
        dataClassifications: [
            AiDataClassification::ProductKnowledge->value,
            AiDataClassification::OperationalMetadata->value,
        ],
        sourceFamily: 'product_knowledge',
        callerSurface: 'product_knowledge',
        contextFingerprint: 'product_knowledge:answer:v1',
    )));

    expect($decision->isBlocked())->toBeTrue()
        ->and($decision->reasonCode)->toBe(AiDecisionReasonCode::ProviderClassBlocked)
        ->and($decision->matchedOperationalControlScope)->toBeNull();
});

it('blocks disallowed data classifications before any provider resolution', function (): void {
    [$workspace, $user] = aiPolicyWorkspace();
    $tenant = ManagedEnvironment::factory()->create(['workspace_id' => (int) $workspace->getKey()]);

    $decision = assertNoOutboundHttp(fn () => app(GovernedAiExecutionBoundary::class)->evaluate(new AiExecutionRequest(
        workspace: $workspace,
        tenant: $tenant,
        actor: $user,
        useCaseKey: 'support_diagnostics.summary_draft',
        requestedProviderClass: AiProviderClass::LocalPrivate->value,
        dataClassifications: [AiDataClassification::RawProviderPayload->value],
        sourceFamily: 'support_diagnostics',
        callerSurface: 'support_diagnostics',
        contextFingerprint: 'support_diagnostics:raw:v1',
    )));

    expect($decision->isBlocked())->toBeTrue()
        ->and($decision->reasonCode)->toBe(AiDecisionReasonCode::DataClassificationBlocked);
});

it('blocks unregistered use cases', function (): void {
    [$workspace, $user] = aiPolicyWorkspace();

    $decision = assertNoOutboundHttp(fn () => app(GovernedAiExecutionBoundary::class)->evaluate(new AiExecutionRequest(
        workspace: $workspace,
        tenant: null,
        actor: $user,
        useCaseKey: 'customer_email.reply',
        requestedProviderClass: AiProviderClass::LocalPrivate->value,
        dataClassifications: [AiDataClassification::ProductKnowledge->value],
        sourceFamily: 'product_knowledge',
        callerSurface: 'product_knowledge',
        contextFingerprint: 'customer_email:reply:v1',
    )));

    expect($decision->isBlocked())->toBeTrue()
        ->and($decision->reasonCode)->toBe(AiDecisionReasonCode::UnregisteredUseCase);
});

it('lets the ai execution operational control override an otherwise valid request', function (): void {
    [$workspace, $user] = aiPolicyWorkspace();

    OperationalControlActivation::factory()->forGlobalScope()->create([
        'control_key' => 'ai.execution',
        'reason_text' => 'Paused for AI rollout review.',
    ]);

    $decision = assertNoOutboundHttp(fn () => app(GovernedAiExecutionBoundary::class)->evaluate(new AiExecutionRequest(
        workspace: $workspace,
        tenant: null,
        actor: $user,
        useCaseKey: 'product_knowledge.answer_draft',
        requestedProviderClass: AiProviderClass::LocalPrivate->value,
        dataClassifications: [
            AiDataClassification::ProductKnowledge->value,
            AiDataClassification::OperationalMetadata->value,
        ],
        sourceFamily: 'product_knowledge',
        callerSurface: 'product_knowledge',
        contextFingerprint: 'product_knowledge:answer:v1',
    )));

    expect($decision->isBlocked())->toBeTrue()
        ->and($decision->reasonCode)->toBe(AiDecisionReasonCode::OperationalControlPaused)
        ->and($decision->matchedOperationalControlScope)->toBe('global');
});
