<?php

declare(strict_types=1);

namespace App\Support\ReasonTranslation;

use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Support\Operations\ExecutionDenialReasonCode;
use App\Support\Operations\LifecycleReconciliationReason;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Providers\ProviderReasonTranslator;
use App\Support\RbacReason;
use App\Support\Tenants\TenantOperabilityReasonCode;
use Illuminate\Support\Str;

final class ReasonPresenter
{
    public const string GOVERNANCE_ARTIFACT_TRUTH_ARTIFACT = ReasonTranslator::GOVERNANCE_ARTIFACT_TRUTH_ARTIFACT;

    public function __construct(
        private readonly ReasonTranslator $reasonTranslator,
    ) {}

    public function forOperationRun(OperationRun $run, string $surface = 'detail'): ?ReasonResolutionEnvelope
    {
        $context = is_array($run->context) ? $run->context : [];
        $storedTranslation = $this->storedOperationRunTranslation($context);

        if ($storedTranslation !== null) {
            $storedEnvelope = ReasonResolutionEnvelope::fromArray($storedTranslation);

            if ($storedEnvelope instanceof ReasonResolutionEnvelope) {
                $nextSteps = $this->operationRunNextSteps($context);

                if ($storedEnvelope->nextSteps === [] && $nextSteps !== []) {
                    return $storedEnvelope->withNextSteps($nextSteps);
                }

                return $storedEnvelope;
            }
        }

        $contextReasonCode = data_get($context, 'execution_legitimacy.reason_code')
            ?? data_get($context, 'reason_code')
            ?? data_get($context, 'baseline_capture.reason_code')
            ?? data_get($context, 'baseline_compare.reason_code');

        if (is_string($contextReasonCode) && trim($contextReasonCode) !== '') {
            return $this->translateOperationRunReason(trim($contextReasonCode), $surface, $context);
        }

        $failureReasonCode = data_get($run->failure_summary, '0.reason_code');

        if (! is_string($failureReasonCode) || trim($failureReasonCode) === '') {
            return null;
        }

        $failureReasonCode = trim($failureReasonCode);

        if (! $this->isDirectlyTranslatableOperationReason($failureReasonCode)) {
            return null;
        }

        $envelope = $this->translateOperationRunReason($failureReasonCode, $surface, $context);

        if (! $envelope instanceof ReasonResolutionEnvelope) {
            return null;
        }

        if ($envelope->nextSteps !== []) {
            return $envelope;
        }

        $legacyNextSteps = $this->operationRunNextSteps($context);

        return $legacyNextSteps !== [] ? $envelope->withNextSteps($legacyNextSteps) : $envelope;
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array<string, mixed>|null
     */
    private function storedOperationRunTranslation(array $context): ?array
    {
        $storedTranslation = $context['reason_translation'] ?? data_get($context, 'baseline_compare.reason_translation');

        return is_array($storedTranslation) ? $storedTranslation : null;
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array<int, NextStepOption>
     */
    private function operationRunNextSteps(array $context): array
    {
        $nextSteps = $context['next_steps'] ?? data_get($context, 'baseline_compare.next_steps');

        return is_array($nextSteps) ? NextStepOption::collect($nextSteps) : [];
    }

    /**
     * @param  array<string, mixed>  $context
     */
    private function translateOperationRunReason(
        string $reasonCode,
        string $surface,
        array $context,
    ): ?ReasonResolutionEnvelope {
        return $this->reasonTranslator->translate($reasonCode, surface: $surface, context: $context);
    }

    private function isDirectlyTranslatableOperationReason(string $reasonCode): bool
    {
        if ($reasonCode === ProviderReasonCodes::UnknownError) {
            return false;
        }

        return ProviderReasonCodes::isKnown($reasonCode)
            || ExecutionDenialReasonCode::tryFrom($reasonCode) instanceof ExecutionDenialReasonCode
            || LifecycleReconciliationReason::tryFrom($reasonCode) instanceof LifecycleReconciliationReason
            || TenantOperabilityReasonCode::tryFrom($reasonCode) instanceof TenantOperabilityReasonCode
            || RbacReason::tryFrom($reasonCode) instanceof RbacReason;
    }

    public function forProviderReason(
        ManagedEnvironment $tenant,
        string $reasonCode,
        ?ProviderConnection $connection = null,
        string $surface = 'detail',
    ): ?ReasonResolutionEnvelope {
        return $this->reasonTranslator->translate(
            reasonCode: $reasonCode,
            artifactKey: ProviderReasonTranslator::ARTIFACT_KEY,
            surface: $surface,
            context: [
                'tenant' => $tenant,
                'connection' => $connection,
            ],
        );
    }

    public function forTenantOperabilityReason(
        TenantOperabilityReasonCode|string|null $reasonCode,
        string $surface = 'detail',
    ): ?ReasonResolutionEnvelope {
        $normalizedCode = $reasonCode instanceof TenantOperabilityReasonCode ? $reasonCode->value : $reasonCode;

        return $this->reasonTranslator->translate(
            reasonCode: $normalizedCode,
            artifactKey: ReasonTranslator::TENANT_OPERABILITY_ARTIFACT,
            surface: $surface,
        );
    }

    public function forRbacReason(RbacReason|string|null $reasonCode, string $surface = 'detail'): ?ReasonResolutionEnvelope
    {
        $normalizedCode = $reasonCode instanceof RbacReason ? $reasonCode->value : $reasonCode;

        return $this->reasonTranslator->translate(
            reasonCode: $normalizedCode,
            artifactKey: ReasonTranslator::RBAC_ARTIFACT,
            surface: $surface,
        );
    }

    /**
     * @param  array<string, mixed>  $context
     */
    public function forArtifactTruth(
        ?string $reasonCode,
        string $surface = 'detail',
        array $context = [],
    ): ?ReasonResolutionEnvelope {
        return $this->reasonTranslator->translate(
            reasonCode: $reasonCode,
            artifactKey: self::GOVERNANCE_ARTIFACT_TRUTH_ARTIFACT,
            surface: $surface,
            context: $context,
        );
    }

    public function diagnosticCode(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->diagnosticCode();
    }

    public function primaryLabel(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->operatorLabel;
    }

    public function shortExplanation(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->shortExplanation;
    }

    public function dominantCauseLabel(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->operatorLabel;
    }

    public function dominantCauseExplanation(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->shortExplanation;
    }

    public function trustImpact(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->trustImpact;
    }

    public function ownerLayer(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->ownerLayer();
    }

    public function ownerNamespace(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->ownerNamespace();
    }

    public function platformReasonFamily(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->platformReasonFamily();
    }

    public function platformReasonFamilyLabel(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->platformReasonFamilyEnum()?->label();
    }

    public function ownerLabel(?ReasonResolutionEnvelope $envelope): ?string
    {
        $ownerNamespace = $envelope?->ownerNamespace();

        if (! is_string($ownerNamespace) || trim($ownerNamespace) === '') {
            return null;
        }

        return match (true) {
            str_starts_with($ownerNamespace, 'provider.') => 'Provider-owned detail',
            str_starts_with($ownerNamespace, 'governance.') => 'Governance detail',
            $ownerNamespace === 'rbac.intune' => 'Intune RBAC detail',
            $ownerNamespace === 'tenant_operability',
            $ownerNamespace === 'execution_denial',
            $ownerNamespace === 'operation_lifecycle',
            $ownerNamespace === 'reason_translation.fallback' => 'Platform core',
            default => match ($envelope?->ownerLayer()) {
                'provider_owned' => 'Provider-owned detail',
                'domain_owned' => 'Domain-owned detail',
                'platform_core' => 'Platform core',
                'compatibility_alias' => 'Compatibility alias',
                'compatibility_only' => 'Compatibility-only detail',
                default => Str::of((string) $envelope?->ownerLayer())->replace('_', ' ')->headline()->toString(),
            },
        };
    }

    /**
     * @return array{
     *     owner_label: string,
     *     owner_layer: string,
     *     owner_namespace: string,
     *     boundary_classification: ?string,
     *     boundary_label: ?string,
     *     family: string,
     *     family_label: string,
     *     diagnostic_code: string
     * }|null
     */
    public function semantics(?ReasonResolutionEnvelope $envelope): ?array
    {
        if (! $envelope instanceof ReasonResolutionEnvelope) {
            return null;
        }

        $boundary = $this->reasonTranslator->boundaryClassificationForEnvelope($envelope);
        $family = $envelope->platformReasonFamilyEnum();
        $ownerLabel = $this->ownerLabel($envelope);

        if (! is_string($ownerLabel) || $family === null) {
            return null;
        }

        return [
            'owner_label' => $ownerLabel,
            'owner_layer' => (string) $envelope->ownerLayer(),
            'owner_namespace' => (string) $envelope->ownerNamespace(),
            'boundary_classification' => $boundary,
            'boundary_label' => is_string($boundary)
                ? Str::of($boundary)->replace('_', ' ')->headline()->toString()
                : null,
            'family' => $family->value,
            'family_label' => $family->label(),
            'diagnostic_code' => $envelope->diagnosticCode(),
        ];
    }

    public function absencePattern(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->absencePattern;
    }

    public function guidance(?ReasonResolutionEnvelope $envelope): ?string
    {
        return $envelope?->guidanceText();
    }

    /**
     * @return array<int, string>
     */
    public function bodyLines(?ReasonResolutionEnvelope $envelope, bool $includeGuidance = true): array
    {
        return $envelope?->toBodyLines($includeGuidance) ?? [];
    }
}
