<?php

namespace App\Support\Badges;

use BackedEnum;
use Stringable;
use Throwable;

final class BadgeCatalog
{
    /**
     * @var array<string, class-string<BadgeMapper>>
     */
    private const DOMAIN_MAPPERS = [
        BadgeDomain::OperationRunStatus->value => Domains\OperationRunStatusBadge::class,
        BadgeDomain::OperationRunOutcome->value => Domains\OperationRunOutcomeBadge::class,
        BadgeDomain::InventorySyncRunStatus->value => Domains\InventorySyncRunStatusBadge::class,
        BadgeDomain::BackupScheduleRunStatus->value => Domains\BackupScheduleRunStatusBadge::class,
        BadgeDomain::BackupSetStatus->value => Domains\BackupSetStatusBadge::class,
        BadgeDomain::EntraGroupSyncRunStatus->value => Domains\EntraGroupSyncRunStatusBadge::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::BooleanEnabled->value => Domains\BooleanEnabledBadge::class,
        BadgeDomain::BooleanHasErrors->value => Domains\BooleanHasErrorsBadge::class,
        BadgeDomain::TenantStatus->value => Domains\TenantStatusBadge::class,
        BadgeDomain::TenantAppStatus->value => Domains\TenantAppStatusBadge::class,
        BadgeDomain::TenantRbacStatus->value => Domains\TenantRbacStatusBadge::class,
        BadgeDomain::TenantPermissionStatus->value => Domains\TenantPermissionStatusBadge::class,
        BadgeDomain::PolicySnapshotMode->value => Domains\PolicySnapshotModeBadge::class,
        BadgeDomain::PolicyRestoreMode->value => Domains\PolicyRestoreModeBadge::class,
        BadgeDomain::PolicyRisk->value => Domains\PolicyRiskBadge::class,
        BadgeDomain::IgnoredAt->value => Domains\IgnoredAtBadge::class,
        BadgeDomain::RestorePreviewDecision->value => Domains\RestorePreviewDecisionBadge::class,
        BadgeDomain::RestoreResultStatus->value => Domains\RestoreResultStatusBadge::class,
        BadgeDomain::ProviderConnectionStatus->value => Domains\ProviderConnectionStatusBadge::class,
        BadgeDomain::ProviderConnectionHealth->value => Domains\ProviderConnectionHealthBadge::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();
        }
    }

    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;
    }

    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;
    }

    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;
    }
}
