<?php

declare(strict_types=1);

namespace App\Support\ProductKnowledge;

use App\Models\ProviderConnection;
use App\Models\ManagedEnvironment;
use App\Support\Ai\AiDataClassification;
use App\Support\Governance\PlatformVocabularyGlossary;
use App\Support\Links\RequiredPermissionsLinks;
use App\Support\ReasonTranslation\ReasonPresenter;
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
use App\Support\Providers\ProviderReasonCodes;
use App\Support\Ui\GovernanceArtifactTruth\ArtifactTruthEnvelope;
use App\Support\Ui\OperatorExplanation\OperatorExplanationBuilder;
use InvalidArgumentException;

final class ContextualHelpResolver
{
    public function __construct(
        private readonly ContextualHelpCatalog $catalog,
        private readonly PlatformVocabularyGlossary $glossary,
        private readonly ReasonPresenter $reasonPresenter,
        private readonly OperatorExplanationBuilder $operatorExplanationBuilder,
    ) {}

    /**
     * @param  array<string, mixed>  $context
     * @return array{
     *     topic_key: string,
     *     surface_families: list<string>,
     *     headline: string,
     *     short_explanation: string,
     *     troubleshooting_steps: list<string>,
     *     safe_next_action: string,
     *     docs_links: list<array{
     *         label: string,
     *         kind: string,
     *         url: ?string,
     *         resolver: ?string
     *     }>,
     *     canonical_terms: list<string>,
     *     reason_label: ?string,
     *     diagnostic_code: ?string,
     *     operator_summary: ?array{
     *         primaryReason: string,
     *         nextActionText: string,
     *         diagnosticsAvailable: bool,
     *         diagnosticsSummary: ?string
     *     }
     * }
     */
    public function resolve(string $topicKey, array $context = []): array
    {
        $definition = $this->catalog->definition($topicKey);
        $reasonEnvelope = $this->providerReasonEnvelope($context);
        $operatorSummary = $this->operatorSummary($context);

        return [
            'topic_key' => $definition['topic_key'],
            'surface_families' => $definition['surface_families'],
            'headline' => $this->firstNonEmpty(
                $this->stringOrNull($context['headline'] ?? null),
                $definition['headline'],
            ),
            'short_explanation' => $this->firstNonEmpty(
                $this->stringOrNull($context['short_explanation'] ?? null),
                $this->reasonPresenter->shortExplanation($reasonEnvelope),
                $definition['short_explanation'],
            ),
            'troubleshooting_steps' => $this->troubleshootingSteps($definition['troubleshooting_steps'], $context),
            'safe_next_action' => $this->firstNonEmpty(
                $this->stringOrNull($context['safe_next_action'] ?? null),
                $operatorSummary['nextActionText'] ?? null,
                $definition['safe_next_action'],
            ),
            'docs_links' => $this->resolveLinks($definition['docs_links'], $context),
            'canonical_terms' => $this->canonicalTerms($definition['glossary_terms']),
            'reason_label' => $this->reasonPresenter->primaryLabel($reasonEnvelope),
            'diagnostic_code' => $this->reasonPresenter->diagnosticCode($reasonEnvelope),
            'operator_summary' => $operatorSummary,
        ];
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array{
     *     topic_key: string,
     *     surface_families: list<string>,
     *     headline: string,
     *     short_explanation: string,
     *     troubleshooting_steps: list<string>,
     *     safe_next_action: string,
     *     docs_links: list<array{
     *         label: string,
     *         kind: string,
     *         url: ?string,
     *         resolver: ?string
     *     }>,
     *     canonical_terms: list<string>,
     *     reason_label: ?string,
     *     diagnostic_code: ?string,
     *     operator_summary: ?array{
     *         primaryReason: string,
     *         nextActionText: string,
     *         diagnosticsAvailable: bool,
     *         diagnosticsSummary: ?string
     *     }
     * }|null
     */
    public function tryResolve(?string $topicKey, array $context = []): ?array
    {
        if (! is_string($topicKey) || trim($topicKey) === '') {
            return null;
        }

        try {
            return $this->resolve($topicKey, $context);
        } catch (InvalidArgumentException) {
            return null;
        }
    }

    /**
     * @return array{
     *     version: int,
     *     topic_count: int,
     *     topics: list<array{
     *         topic_key: string,
     *         surface_families: list<string>,
     *         headline: string,
     *         short_explanation: string,
     *         troubleshooting_steps: list<string>,
     *         safe_next_action: string,
     *         glossary_terms: list<string>,
     *         docs_links: list<array{
     *             label: string,
     *             kind: string,
     *             url: ?string,
     *             resolver: ?string
     *         }>
     *     }>
     * }
     */
    public function knowledgeSource(): array
    {
        return $this->catalog->knowledgeSource();
    }

    /**
     * @return array{
     *     use_case_key: string,
     *     source_family: string,
     *     data_classifications: list<string>,
     *     operational_metadata: array{version: int, topic_count: int},
     *     topics: list<array{
     *         topic_key: string,
     *         surface_families: list<string>,
     *         headline: string,
     *         short_explanation: string,
     *         troubleshooting_steps: list<string>,
     *         safe_next_action: string,
     *         glossary_terms: list<string>,
     *         docs_links: list<array{label: string, kind: string, url: ?string, resolver: ?string}>
     *     }>
     * }
     */
    public function aiProductKnowledgeAnswerDraftSource(): array
    {
        $source = $this->knowledgeSource();

        return [
            'use_case_key' => 'product_knowledge.answer_draft',
            'source_family' => 'product_knowledge',
            'data_classifications' => [
                AiDataClassification::ProductKnowledge->value,
                AiDataClassification::OperationalMetadata->value,
            ],
            'operational_metadata' => [
                'version' => (int) $source['version'],
                'topic_count' => (int) $source['topic_count'],
            ],
            'topics' => $source['topics'],
        ];
    }

    /**
     * @param  array<string, mixed>|null  $verificationReport
     */
    public function primaryReasonCodeFromVerificationReport(?array $verificationReport): ?string
    {
        foreach ($this->relevantChecks($verificationReport) as $check) {
            $reasonCode = $check['reason_code'] ?? null;

            if (is_string($reasonCode) && trim($reasonCode) !== '') {
                return trim($reasonCode);
            }
        }

        return null;
    }

    public function topicKeyForOnboardingVerification(
        ?string $reasonCode,
        bool $isVerificationStale,
        ?string $verificationOverall,
        ?string $runOutcome,
    ): ?string {
        if ($isVerificationStale) {
            return ContextualHelpCatalog::VERIFICATION_STALE;
        }

        $reasonTopicKey = $this->onboardingTopicKeyForReason($reasonCode);

        if ($reasonTopicKey !== null) {
            return $reasonTopicKey;
        }

        return $this->isVerificationFailure($verificationOverall, $runOutcome)
            ? ContextualHelpCatalog::VERIFICATION_FAILED
            : null;
    }

    public function topicKeyForSupportDiagnostics(
        ?string $reasonCode,
        bool $hasIncompleteEvidence,
        ?string $runOutcome,
    ): ?string {
        $reasonTopicKey = $this->supportDiagnosticsTopicKeyForReason($reasonCode);

        if ($reasonTopicKey !== null) {
            return $reasonTopicKey;
        }

        if (is_string($reasonCode) && trim($reasonCode) !== '') {
            return $reasonCode === ProviderReasonCodes::UnknownError
                ? ContextualHelpCatalog::MANUAL_HANDOFF_REQUIRED
                : null;
        }

        if ($this->isVerificationFailure(null, $runOutcome)) {
            return ContextualHelpCatalog::VERIFICATION_FAILED;
        }

        return $hasIncompleteEvidence
            ? ContextualHelpCatalog::DIAGNOSTIC_EVIDENCE_INCOMPLETE
            : null;
    }

    /**
     * @param  array<string, mixed>  $context
     */
    private function providerReasonEnvelope(array $context): ?ReasonResolutionEnvelope
    {
        $tenant = $context['tenant'] ?? null;
        $reasonCode = $context['reason_code'] ?? null;
        $connection = $context['connection'] ?? null;
        $surface = $this->stringOrNull($context['surface'] ?? null) ?? 'detail';

        if (! $tenant instanceof ManagedEnvironment || ! is_string($reasonCode) || trim($reasonCode) === '') {
            return null;
        }

        return $this->reasonPresenter->forProviderReason(
            tenant: $tenant,
            reasonCode: trim($reasonCode),
            connection: $connection instanceof ProviderConnection ? $connection : null,
            surface: $surface,
        );
    }

    /**
     * @param  array<string, mixed>  $context
     * @return array{
     *     primaryReason: string,
     *     nextActionText: string,
     *     diagnosticsAvailable: bool,
     *     diagnosticsSummary: ?string
     * }|null
     */
    private function operatorSummary(array $context): ?array
    {
        $truth = $context['artifact_truth'] ?? null;

        if (! $truth instanceof ArtifactTruthEnvelope) {
            return null;
        }

        return $this->operatorExplanationBuilder->compressionSummaryInputs($truth);
    }

    /**
     * @param  array<string, mixed>|null  $verificationReport
     * @return list<array<string, mixed>>
     */
    private function relevantChecks(?array $verificationReport): array
    {
        $checks = is_array($verificationReport['checks'] ?? null)
            ? $verificationReport['checks']
            : [];

        return array_values(array_filter($checks, static function (mixed $check): bool {
            if (! is_array($check)) {
                return false;
            }

            $status = $check['status'] ?? null;

            return is_string($status)
                && ! in_array($status, ['pass', 'skip', 'running'], true);
        }));
    }

    private function onboardingTopicKeyForReason(?string $reasonCode): ?string
    {
        return match ($reasonCode) {
            ProviderReasonCodes::ProviderConsentMissing,
            ProviderReasonCodes::ProviderConsentFailed,
            ProviderReasonCodes::ProviderConsentRevoked => ContextualHelpCatalog::ADMIN_CONSENT_REQUIRED,
            ProviderReasonCodes::ProviderPermissionMissing,
            ProviderReasonCodes::ProviderPermissionDenied,
            ProviderReasonCodes::IntuneRbacPermissionMissing => ContextualHelpCatalog::REQUIRED_PERMISSIONS_MISSING,
            ProviderReasonCodes::ProviderConnectionMissing,
            ProviderReasonCodes::ProviderConnectionInvalid,
            ProviderReasonCodes::ProviderCredentialMissing,
            ProviderReasonCodes::ProviderCredentialInvalid,
            ProviderReasonCodes::ProviderConnectionTypeInvalid,
            ProviderReasonCodes::DedicatedCredentialMissing,
            ProviderReasonCodes::DedicatedCredentialInvalid,
            ProviderReasonCodes::ProviderAuthFailed,
            ProviderReasonCodes::ProviderConnectionReviewRequired,
            ProviderReasonCodes::ProviderBindingUnsupported,
            ProviderReasonCodes::TenantTargetMismatch,
            ProviderReasonCodes::PlatformIdentityMissing,
            ProviderReasonCodes::PlatformIdentityIncomplete,
            ProviderReasonCodes::IntuneRbacNotConfigured,
            ProviderReasonCodes::IntuneRbacUnhealthy,
            ProviderReasonCodes::IntuneRbacStale => ContextualHelpCatalog::CONNECTION_UNHEALTHY,
            default => null,
        };
    }

    private function supportDiagnosticsTopicKeyForReason(?string $reasonCode): ?string
    {
        if ($reasonCode === ProviderReasonCodes::ProviderPermissionRefreshFailed
            || $reasonCode === ProviderReasonCodes::NetworkUnreachable
            || $reasonCode === ProviderReasonCodes::RateLimited) {
            return ContextualHelpCatalog::RETRYABLE_PROVIDER_FAILURE;
        }

        return $this->onboardingTopicKeyForReason($reasonCode);
    }

    /**
     * @param  list<string>  $defaultSteps
     * @param  array<string, mixed>  $context
     * @return list<string>
     */
    private function troubleshootingSteps(array $defaultSteps, array $context): array
    {
        $contextSteps = $context['troubleshooting_steps'] ?? [];

        if (! is_array($contextSteps)) {
            return $defaultSteps;
        }

        $normalizedContextSteps = array_values(array_filter(array_map(
            fn (mixed $step): ?string => $this->stringOrNull($step),
            $contextSteps,
        )));

        if ($normalizedContextSteps === []) {
            return $defaultSteps;
        }

        return array_values(array_unique([...$defaultSteps, ...$normalizedContextSteps]));
    }

    /**
     * @param  list<array{label: string, kind: string, url?: string, resolver?: string}>  $links
     * @param  array<string, mixed>  $context
     * @return list<array{label: string, kind: string, url: ?string, resolver: ?string}>
     */
    private function resolveLinks(array $links, array $context): array
    {
        return array_values(array_filter(array_map(
            fn (array $link): ?array => $this->resolveLink($link, $context['tenant'] ?? null),
            $links,
        )));
    }

    /**
     * @param  array{label: string, kind: string, url?: string, resolver?: string}  $link
     * @return array{label: string, kind: string, url: ?string, resolver: ?string}|null
     */
    private function resolveLink(array $link, mixed $tenant): ?array
    {
        $label = $this->stringOrNull($link['label'] ?? null);

        if ($label === null) {
            return null;
        }

        $resolved = [
            'label' => $label,
            'kind' => $this->stringOrNull($link['kind'] ?? null) ?? 'url',
            'url' => $this->stringOrNull($link['url'] ?? null),
            'resolver' => $this->stringOrNull($link['resolver'] ?? null),
        ];

        if (! $tenant instanceof ManagedEnvironment || $resolved['resolver'] === null) {
            return $resolved;
        }

        $resolved['url'] = match ($resolved['resolver']) {
            ContextualHelpCatalog::LINK_RESOLVER_ADMIN_CONSENT_PRIMARY => RequiredPermissionsLinks::adminConsentPrimaryUrl($tenant),
            ContextualHelpCatalog::LINK_RESOLVER_REQUIRED_PERMISSIONS => RequiredPermissionsLinks::requiredPermissions($tenant),
            default => $resolved['url'],
        };

        return $resolved;
    }

    /**
     * @param  list<string>  $terms
     * @return list<string>
     */
    private function canonicalTerms(array $terms): array
    {
        return array_values(array_unique(array_filter(array_map(function (string $term): ?string {
            $canonical = $this->glossary->canonicalName($term);

            return $canonical !== null ? trim($canonical) : $this->stringOrNull($term);
        }, $terms))));
    }

    private function isVerificationFailure(?string $verificationOverall, ?string $runOutcome): bool
    {
        return in_array($verificationOverall, ['blocked', 'needs_attention'], true)
            || in_array($runOutcome, ['failed', 'blocked', 'partially_succeeded'], true);
    }

    private function stringOrNull(mixed $value): ?string
    {
        if (! is_string($value)) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    private function firstNonEmpty(?string ...$values): string
    {
        foreach ($values as $value) {
            if ($value !== null && trim($value) !== '') {
                return $value;
            }
        }

        return '';
    }
}
