<?php

declare(strict_types=1);

namespace App\Support\Providers;

use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Support\Links\RequiredPermissionsLinks;
use App\Support\ManagedEnvironmentLinks;
use App\Support\ReasonTranslation\Contracts\TranslatesReasonCode;
use App\Support\ReasonTranslation\NextStepOption;
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;

final class ProviderReasonTranslator implements TranslatesReasonCode
{
    public const string ARTIFACT_KEY = 'provider_reason_codes';

    public function artifactKey(): string
    {
        return self::ARTIFACT_KEY;
    }

    public function canTranslate(string $reasonCode): bool
    {
        return ProviderReasonCodes::isKnown(trim($reasonCode));
    }

    /**
     * @param  array<string, mixed>  $context
     */
    public function translate(string $reasonCode, string $surface = 'detail', array $context = []): ?ReasonResolutionEnvelope
    {
        $reasonCode = trim($reasonCode);

        if ($reasonCode === '') {
            return null;
        }

        $normalizedCode = ProviderReasonCodes::isKnown($reasonCode)
            ? $reasonCode
            : ProviderReasonCodes::UnknownError;
        $tenant = $context['tenant'] ?? null;
        $connection = $context['connection'] ?? null;
        $capabilityContext = $this->capabilityContext($context);

        if (! $tenant instanceof ManagedEnvironment) {
            $nextSteps = $this->fallbackNextSteps($normalizedCode);
        } else {
            $nextSteps = $this->nextStepsFor($tenant, $normalizedCode, $connection instanceof ProviderConnection ? $connection : null);
        }

        return match ($normalizedCode) {
            ProviderReasonCodes::ProviderConnectionMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Provider connection required',
                shortExplanation: 'This tenant does not have a usable provider connection for Microsoft operations.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConnectionInvalid => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Provider connection needs review',
                shortExplanation: 'The selected provider connection is incomplete or no longer valid for this workflow.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderCredentialMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Credentials missing',
                shortExplanation: 'The provider connection is missing the credentials required to authenticate.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderCredentialInvalid => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Credentials need review',
                shortExplanation: 'Stored provider credentials are no longer valid for the selected provider connection.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConnectionTypeInvalid => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Connection type unsupported',
                shortExplanation: 'The selected provider connection type cannot be used for this workflow.',
                actionability: 'permanent_configuration',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::PlatformIdentityMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Platform identity missing',
                shortExplanation: 'The platform provider connection is missing the app identity details required to continue.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::PlatformIdentityIncomplete => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Platform identity incomplete',
                shortExplanation: 'The platform provider connection needs more app identity details before it can continue.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::DedicatedCredentialMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Dedicated credentials required',
                shortExplanation: 'This dedicated provider connection cannot continue until dedicated credentials are configured.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::DedicatedCredentialInvalid => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Dedicated credentials need review',
                shortExplanation: 'The dedicated credentials are no longer valid for this provider connection.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConsentMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Admin consent required',
                shortExplanation: 'The provider connection cannot continue until admin consent is granted.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConsentFailed => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Admin consent check failed',
                shortExplanation: 'TenantPilot could not confirm admin consent for this provider connection.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConsentRevoked => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Admin consent revoked',
                shortExplanation: 'Previously granted admin consent is no longer valid for this provider connection.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderConnectionReviewRequired => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Connection classification needs review',
                shortExplanation: 'TenantPilot needs you to confirm how this provider connection should be used.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderBindingUnsupported => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Provider binding unsupported',
                shortExplanation: 'This operation does not have an explicit provider binding for the selected provider.',
                actionability: 'permanent_configuration',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderAuthFailed => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Provider authentication failed',
                shortExplanation: 'The provider connection could not authenticate with the stored credentials.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderPermissionMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: is_array($capabilityContext)
                    ? sprintf('%s capability missing', (string) $capabilityContext['label'])
                    : 'Permissions missing',
                shortExplanation: is_array($capabilityContext)
                    ? sprintf('%s capability is missing required provider permissions.', (string) $capabilityContext['label'])
                    : 'The provider app is missing required Microsoft Graph permissions.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderPermissionDenied => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: is_array($capabilityContext)
                    ? sprintf('%s capability denied', (string) $capabilityContext['label'])
                    : 'Permission denied',
                shortExplanation: is_array($capabilityContext)
                    ? sprintf('%s capability could not be used with the current provider permission evidence.', (string) $capabilityContext['label'])
                    : 'Microsoft Graph denied the requested permission for this provider connection.',
                actionability: 'permanent_configuration',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::ProviderPermissionRefreshFailed => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: is_array($capabilityContext)
                    ? sprintf('%s capability needs refresh', (string) $capabilityContext['label'])
                    : 'Permission refresh failed',
                shortExplanation: is_array($capabilityContext)
                    ? sprintf('%s capability needs refreshed provider permission evidence before this operation can continue.', (string) $capabilityContext['label'])
                    : 'TenantPilot could not refresh the provider permission snapshot.',
                actionability: 'retryable_transient',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::IntuneRbacPermissionMissing => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: is_array($capabilityContext)
                    ? sprintf('%s capability missing', (string) $capabilityContext['label'])
                    : 'Intune RBAC permission missing',
                shortExplanation: is_array($capabilityContext)
                    ? sprintf('%s capability is missing a required provider RBAC prerequisite.', (string) $capabilityContext['label'])
                    : 'The provider app lacks the Intune RBAC permission needed for this workflow.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::TenantTargetMismatch => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Connection targets a different tenant',
                shortExplanation: 'The selected provider connection points to a different Microsoft tenant than the current scope.',
                actionability: 'permanent_configuration',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::NetworkUnreachable => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Microsoft Graph temporarily unreachable',
                shortExplanation: 'TenantPilot could not reach Microsoft Graph or the provider dependency.',
                actionability: 'retryable_transient',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::RateLimited => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Request rate limited',
                shortExplanation: 'Microsoft Graph asked TenantPilot to slow down before retrying.',
                actionability: 'retryable_transient',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::IntuneRbacNotConfigured => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Intune RBAC not configured',
                shortExplanation: 'Intune RBAC has not been configured for this tenant yet.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::IntuneRbacUnhealthy => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Intune RBAC health degraded',
                shortExplanation: 'The latest Intune RBAC health check found a blocking issue.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            ProviderReasonCodes::IntuneRbacStale => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: 'Intune RBAC check is stale',
                shortExplanation: 'The latest Intune RBAC health check is too old to trust for write operations.',
                actionability: 'prerequisite_missing',
                nextSteps: $nextSteps,
            ),
            default => $this->envelope(
                reasonCode: $normalizedCode,
                operatorLabel: str_starts_with($normalizedCode, 'ext.')
                    ? 'Provider configuration needs review'
                    : 'Provider check needs review',
                shortExplanation: 'TenantPilot recorded a provider error that does not yet have a domain-specific translation.',
                actionability: 'permanent_configuration',
                nextSteps: $nextSteps,
            ),
        };
    }

    /**
     * @param  array<int, NextStepOption>  $nextSteps
     */
    private function envelope(
        string $reasonCode,
        string $operatorLabel,
        string $shortExplanation,
        string $actionability,
        array $nextSteps,
    ): ReasonResolutionEnvelope {
        return new ReasonResolutionEnvelope(
            internalCode: $reasonCode,
            operatorLabel: $operatorLabel,
            shortExplanation: $shortExplanation,
            actionability: $actionability,
            nextSteps: $nextSteps,
            showNoActionNeeded: false,
            diagnosticCodeLabel: $reasonCode,
            reasonOwnership: ProviderReasonCodes::ownershipDescriptor($reasonCode),
        );
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array{label:string,status:string}|null
     */
    private function capabilityContext(array $context): ?array
    {
        $candidate = $context['provider_capability'] ?? null;

        if (! is_array($candidate)) {
            $capabilities = $context['provider_capabilities'] ?? null;
            $capabilities = is_array($capabilities) ? $capabilities : [];

            foreach ($capabilities as $capability) {
                if (! is_array($capability)) {
                    continue;
                }

                $status = is_string($capability['status'] ?? null) ? trim((string) $capability['status']) : '';

                if (in_array($status, ['blocked', 'missing', 'unknown'], true)) {
                    $candidate = $capability;
                    break;
                }
            }
        }

        if (! is_array($candidate)) {
            return null;
        }

        $label = is_string($candidate['label'] ?? null) ? trim((string) $candidate['label']) : '';
        $status = is_string($candidate['status'] ?? null) ? trim((string) $candidate['status']) : '';

        if ($label === '' || $status === '') {
            return null;
        }

        return [
            'label' => $label,
            'status' => $status,
        ];
    }

    /**
     * @return array<int, NextStepOption>
     */
    private function fallbackNextSteps(string $reasonCode): array
    {
        return match ($reasonCode) {
            ProviderReasonCodes::NetworkUnreachable, ProviderReasonCodes::RateLimited => [
                NextStepOption::instruction('Retry after the provider dependency recovers.'),
            ],
            ProviderReasonCodes::UnknownError => [
                NextStepOption::instruction('Review the provider connection and retry once the cause is understood.'),
            ],
            default => [
                NextStepOption::instruction('Review the provider connection before retrying.'),
            ],
        };
    }

    /**
     * @return array<int, NextStepOption>
     */
    private function nextStepsFor(
        ManagedEnvironment $tenant,
        string $reasonCode,
        ?ProviderConnection $connection = null,
    ): array {
        return match ($reasonCode) {
            ProviderReasonCodes::ProviderConnectionMissing,
            ProviderReasonCodes::ProviderConnectionInvalid,
            ProviderReasonCodes::TenantTargetMismatch,
            ProviderReasonCodes::PlatformIdentityMissing,
            ProviderReasonCodes::PlatformIdentityIncomplete,
            ProviderReasonCodes::ProviderConnectionReviewRequired,
            ProviderReasonCodes::ProviderBindingUnsupported => [
                NextStepOption::link(
                    label: $connection instanceof ProviderConnection ? 'Review migration classification' : 'Manage Provider Connections',
                    destination: $connection instanceof ProviderConnection
                        ? ManagedEnvironmentLinks::providerConnectionUrl($connection, 'view', $tenant)
                        : ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
                NextStepOption::link(
                    label: 'Review effective app details',
                    destination: $connection instanceof ProviderConnection
                        ? ManagedEnvironmentLinks::providerConnectionUrl($connection, 'edit', $tenant)
                        : ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
            ],
            ProviderReasonCodes::DedicatedCredentialMissing,
            ProviderReasonCodes::DedicatedCredentialInvalid => [
                NextStepOption::link(
                    label: $connection instanceof ProviderConnection ? 'Manage dedicated connection' : 'Manage Provider Connections',
                    destination: $connection instanceof ProviderConnection
                        ? ManagedEnvironmentLinks::providerConnectionUrl($connection, 'edit', $tenant)
                        : ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
            ],
            ProviderReasonCodes::ProviderCredentialMissing,
            ProviderReasonCodes::ProviderCredentialInvalid,
            ProviderReasonCodes::ProviderConsentFailed,
            ProviderReasonCodes::ProviderConsentRevoked,
            ProviderReasonCodes::ProviderAuthFailed,
            ProviderReasonCodes::ProviderConsentMissing => [
                NextStepOption::link(
                    label: 'Grant admin consent',
                    destination: RequiredPermissionsLinks::adminConsentPrimaryUrl($tenant),
                ),
                NextStepOption::link(
                    label: $connection instanceof ProviderConnection
                        ? ($connection->connection_type === ProviderConnectionType::Dedicated ? 'Manage dedicated connection' : 'Review platform connection')
                        : 'Manage Provider Connections',
                    destination: $connection instanceof ProviderConnection
                        ? ManagedEnvironmentLinks::providerConnectionUrl($connection, 'edit', $tenant)
                        : ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
            ],
            ProviderReasonCodes::ProviderPermissionMissing,
            ProviderReasonCodes::ProviderPermissionDenied,
            ProviderReasonCodes::ProviderPermissionRefreshFailed,
            ProviderReasonCodes::IntuneRbacPermissionMissing => [
                NextStepOption::link(
                    label: 'Open Required Permissions',
                    destination: RequiredPermissionsLinks::requiredPermissions($tenant),
                ),
            ],
            ProviderReasonCodes::IntuneRbacNotConfigured,
            ProviderReasonCodes::IntuneRbacUnhealthy,
            ProviderReasonCodes::IntuneRbacStale => [
                NextStepOption::link(
                    label: 'Review provider connections',
                    destination: ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
                NextStepOption::instruction('Refresh the tenant RBAC health check before retrying.', scope: 'tenant'),
            ],
            ProviderReasonCodes::NetworkUnreachable,
            ProviderReasonCodes::RateLimited => [
                NextStepOption::instruction('Retry after the provider dependency recovers.', scope: 'tenant'),
                NextStepOption::link(
                    label: 'Review provider connection',
                    destination: $connection instanceof ProviderConnection
                        ? ManagedEnvironmentLinks::providerConnectionUrl($connection, 'edit', $tenant)
                        : ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
            ],
            default => [
                NextStepOption::link(
                    label: 'Manage Provider Connections',
                    destination: ManagedEnvironmentLinks::providerConnectionsUrl($tenant),
                ),
            ],
        };
    }
}
