<?php

namespace App\Support\Badges;

use App\Support\Tenants\TenantLifecyclePresentation;
use BackedEnum;
use Stringable;
use Throwable;

final class BadgeCatalog
{
    /**
     * @var array<string, class-string<BadgeMapper>>
     */
    private const DOMAIN_MAPPERS = [
        BadgeDomain::AuditOutcome->value => Domains\AuditOutcomeBadge::class,
        BadgeDomain::AuditActorType->value => Domains\AuditActorTypeBadge::class,
        BadgeDomain::GovernanceArtifactExistence->value => Domains\GovernanceArtifactExistenceBadge::class,
        BadgeDomain::GovernanceArtifactContent->value => Domains\GovernanceArtifactContentBadge::class,
        BadgeDomain::GovernanceArtifactFreshness->value => Domains\GovernanceArtifactFreshnessBadge::class,
        BadgeDomain::GovernanceArtifactLifecycle->value => Domains\GovernanceArtifactLifecycleBadge::class,
        BadgeDomain::GovernanceArtifactPublicationReadiness->value => Domains\GovernanceArtifactPublicationReadinessBadge::class,
        BadgeDomain::GovernanceArtifactRetention->value => Domains\GovernanceArtifactRetentionBadge::class,
        BadgeDomain::GovernanceArtifactActionability->value => Domains\GovernanceArtifactActionabilityBadge::class,
        BadgeDomain::OperatorExplanationEvaluationResult->value => Domains\OperatorExplanationEvaluationResultBadge::class,
        BadgeDomain::OperatorExplanationTrustworthiness->value => Domains\OperatorExplanationTrustworthinessBadge::class,
        BadgeDomain::BaselineSnapshotLifecycle->value => Domains\BaselineSnapshotLifecycleBadge::class,
        BadgeDomain::BaselineSnapshotFidelity->value => Domains\BaselineSnapshotFidelityBadge::class,
        BadgeDomain::BaselineSnapshotGapStatus->value => Domains\BaselineSnapshotGapStatusBadge::class,
        BadgeDomain::OperationRunStatus->value => Domains\OperationRunStatusBadge::class,
        BadgeDomain::OperationRunOutcome->value => Domains\OperationRunOutcomeBadge::class,
        BadgeDomain::InventoryCoverageState->value => Domains\InventoryCoverageStateBadge::class,
        BadgeDomain::BackupSetStatus->value => Domains\BackupSetStatusBadge::class,
        BadgeDomain::RestoreRunStatus->value => Domains\RestoreRunStatusBadge::class,
        BadgeDomain::RestoreCheckSeverity->value => Domains\RestoreCheckSeverityBadge::class,
        BadgeDomain::FindingStatus->value => Domains\FindingStatusBadge::class,
        BadgeDomain::FindingSeverity->value => Domains\FindingSeverityBadge::class,
        BadgeDomain::FindingExceptionStatus->value => Domains\FindingExceptionStatusBadge::class,
        BadgeDomain::FindingRiskGovernanceValidity->value => Domains\FindingRiskGovernanceValidityBadge::class,
        BadgeDomain::BooleanEnabled->value => Domains\BooleanEnabledBadge::class,
        BadgeDomain::BooleanHasErrors->value => Domains\BooleanHasErrorsBadge::class,
        BadgeDomain::TenantStatus->value => Domains\TenantStatusBadge::class,
        BadgeDomain::TenantWorkspacePosture->value => Domains\TenantWorkspacePostureBadge::class,
        BadgeDomain::TenantRbacStatus->value => Domains\TenantRbacStatusBadge::class,
        BadgeDomain::ManagedEnvironmentPermissionStatus->value => Domains\ManagedEnvironmentPermissionStatusBadge::class,
        BadgeDomain::PolicySnapshotMode->value => Domains\PolicySnapshotModeBadge::class,
        BadgeDomain::PolicyRestoreMode->value => Domains\PolicyRestoreModeBadge::class,
        BadgeDomain::PolicyRisk->value => Domains\PolicyRiskBadge::class,
        BadgeDomain::PolicyProviderPresence->value => Domains\PolicyProviderPresenceBadge::class,
        BadgeDomain::IgnoredAt->value => Domains\IgnoredAtBadge::class,
        BadgeDomain::RestorePreviewDecision->value => Domains\RestorePreviewDecisionBadge::class,
        BadgeDomain::RestoreResultStatus->value => Domains\RestoreResultStatusBadge::class,
        BadgeDomain::ProviderConsentStatus->value => Domains\ProviderConsentStatusBadge::class,
        BadgeDomain::ProviderVerificationStatus->value => Domains\ProviderVerificationStatusBadge::class,
        BadgeDomain::ProviderCapabilityStatus->value => Domains\ProviderCapabilityStatusBadge::class,
        BadgeDomain::ManagedEnvironmentOnboardingVerificationStatus->value => Domains\ManagedEnvironmentOnboardingVerificationStatusBadge::class,
        BadgeDomain::VerificationCheckStatus->value => Domains\VerificationCheckStatusBadge::class,
        BadgeDomain::VerificationCheckSeverity->value => Domains\VerificationCheckSeverityBadge::class,
        BadgeDomain::VerificationReportOverall->value => Domains\VerificationReportOverallBadge::class,
        BadgeDomain::AlertDeliveryStatus->value => Domains\AlertDeliveryStatusBadge::class,
        BadgeDomain::AlertDestinationLastTestStatus->value => Domains\AlertDestinationLastTestStatusBadge::class,
        BadgeDomain::BaselineProfileStatus->value => Domains\BaselineProfileStatusBadge::class,
        BadgeDomain::FindingType->value => Domains\FindingTypeBadge::class,
        BadgeDomain::ReviewPackStatus->value => Domains\ReviewPackStatusBadge::class,
        BadgeDomain::WorkspaceClosurePosture->value => Domains\WorkspaceClosurePostureBadge::class,
        BadgeDomain::CommercialLifecycleState->value => Domains\CommercialLifecycleStateBadge::class,
        BadgeDomain::EvidenceSnapshotStatus->value => Domains\EvidenceSnapshotStatusBadge::class,
        BadgeDomain::EvidenceCompleteness->value => Domains\EvidenceCompletenessBadge::class,
        BadgeDomain::EnvironmentReviewStatus->value => Domains\EnvironmentReviewStatusBadge::class,
        BadgeDomain::EnvironmentReviewCompleteness->value => Domains\EnvironmentReviewCompletenessStateBadge::class,
        BadgeDomain::ManagedEnvironmentTriageReviewState->value => Domains\ManagedEnvironmentTriageReviewStateBadge::class,
        BadgeDomain::SystemHealth->value => Domains\SystemHealthBadge::class,
        BadgeDomain::ReferenceResolutionState->value => Domains\ReferenceResolutionStateBadge::class,
        BadgeDomain::DiffRowStatus->value => Domains\DiffRowStatusBadge::class,
        BadgeDomain::BaselineCompareMatrixState->value => Domains\BaselineCompareMatrixStateBadge::class,
        BadgeDomain::BaselineCompareMatrixFreshness->value => Domains\BaselineCompareMatrixFreshnessBadge::class,
        BadgeDomain::BaselineCompareMatrixTrust->value => Domains\BaselineCompareMatrixTrustBadge::class,
    ];

    /**
     * @var array<string, BadgeMapper|null>
     */
    private static array $mapperCache = [];

    public static function spec(BadgeDomain $domain, mixed $value): BadgeSpec
    {
        $mapper = self::mapper($domain);

        if (! $mapper) {
            return BadgeSpec::unknown();
        }

        try {
            return $mapper->spec($value);
        } catch (Throwable) {
            return BadgeSpec::unknown();
        }
    }

    /**
     * @return array{label: string, color: ?string, icon: ?string, iconColor: ?string}
     */
    public static function summaryData(BadgeSpec $spec): array
    {
        return [
            'label' => $spec->label,
            'color' => $spec->color,
            'icon' => $spec->icon,
            'iconColor' => $spec->iconColor,
        ];
    }

    public static function mapper(BadgeDomain $domain): ?BadgeMapper
    {
        $key = $domain->value;

        if (array_key_exists($key, self::$mapperCache)) {
            return self::$mapperCache[$key];
        }

        $mapper = self::buildMapper($domain);

        self::$mapperCache[$key] = $mapper;

        return $mapper;
    }

    /**
     * @param  iterable<mixed>  $values
     * @return array<string, string>
     */
    public static function options(BadgeDomain $domain, iterable $values): array
    {
        $options = [];

        foreach ($values as $value) {
            $normalized = self::normalizeState($value);

            if ($normalized === null) {
                continue;
            }

            $options[$normalized] = self::spec($domain, $value)->label;
        }

        return $options;
    }

    public static function normalizeState(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        if ($value instanceof BackedEnum) {
            $value = $value->value;
        }

        if ($value instanceof Stringable) {
            $value = (string) $value;
        }

        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        }

        if (is_int($value) || is_float($value)) {
            return (string) $value;
        }

        if (! is_string($value)) {
            return null;
        }

        $normalized = strtolower(trim($value));
        $normalized = str_replace([' ', '-'], '_', $normalized);

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

    public static function normalizeProviderConsentStatus(mixed $value): ?string
    {
        $state = self::normalizeState($value);

        return match ($state) {
            'needs_admin_consent', 'needs_consent', 'consent_required' => 'required',
            'connected' => 'granted',
            'error' => 'failed',
            default => $state,
        };
    }

    public static function normalizeProviderVerificationStatus(mixed $value): ?string
    {
        $state = self::normalizeState($value);

        return match ($state) {
            'not_started', 'never_checked' => 'unknown',
            'in_progress' => 'pending',
            'ok' => 'healthy',
            'warning', 'needs_attention' => 'degraded',
            'failed' => 'error',
            default => $state,
        };
    }

    public static function normalizeManagedEnvironmentOnboardingVerificationStatus(mixed $value): ?string
    {
        $state = self::normalizeState($value);

        return match ($state) {
            'unknown', 'not_started' => 'not_started',
            'pending', 'in_progress' => 'in_progress',
            'degraded', 'needs_attention' => 'needs_attention',
            'blocked', 'error' => 'blocked',
            'healthy', 'ready' => 'ready',
            default => $state,
        };
    }

    public static function normalizeTenantLifecycle(mixed $value): ?string
    {
        $presentation = self::tenantLifecyclePresentation($value);

        return $presentation->isInvalidFallback ? null : $presentation->value;
    }

    public static function tenantLifecyclePresentation(mixed $value): TenantLifecyclePresentation
    {
        return TenantLifecyclePresentation::fromValue($value);
    }

    private static function buildMapper(BadgeDomain $domain): ?BadgeMapper
    {
        $mapperClass = self::DOMAIN_MAPPERS[$domain->value] ?? null;

        if (! $mapperClass) {
            return null;
        }

        if (! class_exists($mapperClass)) {
            return null;
        }

        $mapper = new $mapperClass;

        return $mapper instanceof BadgeMapper ? $mapper : null;
    }
}
