<?php

declare(strict_types=1);

namespace App\Support\Ai;

use App\Services\Audit\WorkspaceAuditLogger;
use App\Services\Settings\SettingsResolver;
use App\Support\Audit\AuditActionId;
use App\Support\OperationalControls\OperationalControlEvaluator;

final class GovernedAiExecutionBoundary
{
    public function __construct(
        private readonly AiUseCaseCatalog $useCaseCatalog,
        private readonly SettingsResolver $settingsResolver,
        private readonly OperationalControlEvaluator $operationalControls,
        private readonly AiDecisionAuditMetadataFactory $auditMetadataFactory,
        private readonly WorkspaceAuditLogger $workspaceAuditLogger,
    ) {}

    public function evaluate(AiExecutionRequest $request): AiExecutionDecision
    {
        $decision = $this->decisionFor($request);
        $metadata = $this->auditMetadataFactory->make($request, $decision);

        $decision = new AiExecutionDecision(
            outcome: $decision->outcome,
            reasonCode: $decision->reasonCode,
            workspaceAiPolicyMode: $decision->workspaceAiPolicyMode,
            matchedOperationalControlScope: $decision->matchedOperationalControlScope,
            useCaseKey: $decision->useCaseKey,
            requestedProviderClass: $decision->requestedProviderClass,
            dataClassifications: $decision->dataClassifications,
            sourceFamily: $decision->sourceFamily,
            auditAction: $decision->auditAction,
            auditMetadata: $metadata,
        );

        if ($request->workspace !== null) {
            $definition = $this->useCaseCatalog->find($request->useCaseKey);

            $this->workspaceAuditLogger->log(
                workspace: $request->workspace,
                action: $decision->auditAction,
                context: ['metadata' => $decision->auditMetadata],
                actor: $request->actor,
                status: $decision->isAllowed() ? 'success' : 'blocked',
                resourceType: 'ai_use_case',
                resourceId: $request->useCaseKey,
                targetLabel: $definition['label'] ?? $request->useCaseKey,
                summary: 'AI execution decision evaluated',
                tenant: $request->tenant,
            );
        }

        return $decision;
    }

    private function decisionFor(AiExecutionRequest $request): AiExecutionDecision
    {
        if ($request->workspace === null) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::MissingWorkspaceContext,
                workspaceAiPolicyMode: AiPolicyMode::Disabled->value,
            );
        }

        if ($request->tenant !== null && (int) $request->tenant->workspace_id !== (int) $request->workspace->getKey()) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::TenantOutsideWorkspace,
                workspaceAiPolicyMode: AiPolicyMode::Disabled->value,
            );
        }

        $controlDecision = $this->operationalControls->evaluate('ai.execution', $request->workspace);

        if ($controlDecision->isPaused()) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::OperationalControlPaused,
                workspaceAiPolicyMode: $this->resolvedPolicyMode($request),
                matchedOperationalControlScope: $controlDecision->matchedScopeType,
            );
        }

        $policyMode = $this->resolvedPolicyMode($request);

        if ($policyMode === AiPolicyMode::Disabled->value) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::WorkspacePolicyDisabled,
                workspaceAiPolicyMode: $policyMode,
            );
        }

        $definition = $this->useCaseCatalog->find($request->useCaseKey);

        if ($definition === null) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::UnregisteredUseCase,
                workspaceAiPolicyMode: $policyMode,
            );
        }

        if ($definition['source_family'] !== $request->sourceFamily) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::SourceFamilyMismatch,
                workspaceAiPolicyMode: $policyMode,
            );
        }

        if (! in_array($request->requestedProviderClass, $definition['allowed_provider_classes'], true)) {
            return $this->blockedDecision(
                request: $request,
                reasonCode: AiDecisionReasonCode::ProviderClassBlocked,
                workspaceAiPolicyMode: $policyMode,
            );
        }

        foreach ($request->dataClassifications as $classification) {
            if (! in_array($classification, $definition['allowed_data_classifications'], true)) {
                return $this->blockedDecision(
                    request: $request,
                    reasonCode: AiDecisionReasonCode::DataClassificationBlocked,
                    workspaceAiPolicyMode: $policyMode,
                );
            }
        }

        return new AiExecutionDecision(
            outcome: 'allowed',
            reasonCode: AiDecisionReasonCode::Allowed,
            workspaceAiPolicyMode: $policyMode,
            matchedOperationalControlScope: null,
            useCaseKey: $request->useCaseKey,
            requestedProviderClass: $request->requestedProviderClass,
            dataClassifications: $request->dataClassifications,
            sourceFamily: $request->sourceFamily,
            auditAction: AuditActionId::AiExecutionDecisionEvaluated,
            auditMetadata: [],
        );
    }

    private function resolvedPolicyMode(AiExecutionRequest $request): string
    {
        if ($request->workspace === null) {
            return AiPolicyMode::Disabled->value;
        }

        $resolved = $this->settingsResolver->resolveValue($request->workspace, 'ai', 'policy_mode');

        return is_string($resolved) && $resolved !== ''
            ? $resolved
            : AiPolicyMode::Disabled->value;
    }

    private function blockedDecision(
        AiExecutionRequest $request,
        AiDecisionReasonCode $reasonCode,
        string $workspaceAiPolicyMode,
        ?string $matchedOperationalControlScope = null,
    ): AiExecutionDecision {
        return new AiExecutionDecision(
            outcome: 'blocked',
            reasonCode: $reasonCode,
            workspaceAiPolicyMode: $workspaceAiPolicyMode,
            matchedOperationalControlScope: $matchedOperationalControlScope,
            useCaseKey: $request->useCaseKey,
            requestedProviderClass: $request->requestedProviderClass,
            dataClassifications: $request->dataClassifications,
            sourceFamily: $request->sourceFamily,
            auditAction: AuditActionId::AiExecutionDecisionEvaluated,
            auditMetadata: [],
        );
    }
}