<?php

namespace App\Support\Badges;

use App\Support\Inventory\InventoryPolicyTypeMeta;
use BackedEnum;
use Illuminate\Support\Str;
use Stringable;

final class TagBadgeCatalog
{
    /**
     * @var array<string, array<string, mixed>>
     */
    private static array $policyTypeMetaCache = [];

    /**
     * @var array<string, string>
     */
    private static array $policyCategoryLabelCache = [];

    public static function spec(TagBadgeDomain $domain, mixed $value): TagBadgeSpec
    {
        return match ($domain) {
            TagBadgeDomain::PolicyType => self::policyType($value),
            TagBadgeDomain::PolicyCategory => self::policyCategory($value),
            TagBadgeDomain::Platform => self::platform($value),
            TagBadgeDomain::TenantEnvironment => self::tenantEnvironment($value),
            TagBadgeDomain::Visibility => self::visibility($value),
            TagBadgeDomain::SnapshotType => self::snapshotType($value),
            TagBadgeDomain::BackupScheduleFrequency => self::backupScheduleFrequency($value),
        };
    }

    public static function normalizeValue(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;
        }

        $value = trim($value);

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

    private static function policyType(mixed $value): TagBadgeSpec
    {
        $type = self::normalizeValue($value);

        if (! filled($type)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::PolicyType));
        }

        $meta = self::policyTypeMeta($type);
        $label = $meta['label'] ?? null;

        if (is_string($label) && $label !== '') {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::PolicyType));
        }

        return self::other(TagBadgeDomain::PolicyType, $type);
    }

    private static function policyCategory(mixed $value): TagBadgeSpec
    {
        $category = self::normalizeValue($value);

        if (! filled($category)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::PolicyCategory));
        }

        $normalized = Str::of($category)
            ->trim()
            ->lower()
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = self::policyCategoryLabels()[$normalized] ?? null;

        if (is_string($label) && $label !== '') {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::PolicyCategory));
        }

        return self::other(TagBadgeDomain::PolicyCategory, $category);
    }

    private static function platform(mixed $value): TagBadgeSpec
    {
        $platform = self::normalizeValue($value);

        if (! filled($platform)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::Platform));
        }

        $normalized = Str::of($platform)
            ->trim()
            ->lower()
            ->replace(['_', '-'], ' ')
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = match ($normalized) {
            'windows' => __('localization.policy.common.platform_label_windows'),
            'android' => __('localization.policy.common.platform_label_android'),
            'ios' => __('localization.policy.common.platform_label_ios'),
            'macos' => __('localization.policy.common.platform_label_macos'),
            'all' => __('localization.policy.common.platform_label_all'),
            'mobile' => __('localization.policy.common.platform_label_mobile'),
            default => null,
        };

        if (is_string($label)) {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::Platform));
        }

        return self::other(TagBadgeDomain::Platform, $platform);
    }

    private static function tenantEnvironment(mixed $value): TagBadgeSpec
    {
        $environment = self::normalizeValue($value);

        if (! filled($environment)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::TenantEnvironment));
        }

        $normalized = Str::of($environment)
            ->trim()
            ->lower()
            ->replace(['_', '-'], ' ')
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = match ($normalized) {
            'prod', 'production' => 'Prod',
            'staging' => 'Staging',
            'dev', 'development' => 'Dev',
            default => null,
        };

        if (is_string($label)) {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::TenantEnvironment));
        }

        return self::other(TagBadgeDomain::TenantEnvironment, $environment);
    }

    private static function visibility(mixed $value): TagBadgeSpec
    {
        $visibility = self::normalizeValue($value);

        if (! filled($visibility)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::Visibility));
        }

        $normalized = Str::of($visibility)
            ->trim()
            ->lower()
            ->replace(['_', '-'], ' ')
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = match ($normalized) {
            'active' => 'Active',
            'archived' => 'Archived',
            default => null,
        };

        if (is_string($label)) {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::Visibility));
        }

        return self::other(TagBadgeDomain::Visibility, $visibility);
    }

    private static function snapshotType(mixed $value): TagBadgeSpec
    {
        $type = self::normalizeValue($value);

        if (! filled($type)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::SnapshotType));
        }

        $normalized = Str::of($type)
            ->trim()
            ->lower()
            ->replace(['_', '-'], ' ')
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = match ($normalized) {
            'full' => 'Full',
            'incremental' => 'Incremental',
            default => null,
        };

        if (is_string($label)) {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::SnapshotType));
        }

        return self::other(TagBadgeDomain::SnapshotType, $type);
    }

    private static function backupScheduleFrequency(mixed $value): TagBadgeSpec
    {
        $frequency = self::normalizeValue($value);

        if (! filled($frequency)) {
            return new TagBadgeSpec('Other: —', self::colorForDomain(TagBadgeDomain::BackupScheduleFrequency));
        }

        $normalized = Str::of($frequency)
            ->trim()
            ->lower()
            ->replace(['_', '-'], ' ')
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        $label = match ($normalized) {
            'daily' => 'Daily',
            'weekly' => 'Weekly',
            default => null,
        };

        if (is_string($label)) {
            return new TagBadgeSpec($label, self::colorForDomain(TagBadgeDomain::BackupScheduleFrequency));
        }

        return self::other(TagBadgeDomain::BackupScheduleFrequency, $frequency);
    }

    private static function other(TagBadgeDomain $domain, string $value): TagBadgeSpec
    {
        $normalizedValue = Str::of($value)
            ->trim()
            ->replaceMatches('/\s+/', ' ')
            ->toString();

        return new TagBadgeSpec(
            label: 'Other: '.$normalizedValue,
            color: self::colorForDomain($domain),
        );
    }

    private static function colorForDomain(TagBadgeDomain $domain): string
    {
        return match ($domain) {
            TagBadgeDomain::PolicyType => 'primary',
            TagBadgeDomain::PolicyCategory => 'gray',
            TagBadgeDomain::Platform => 'info',
            TagBadgeDomain::TenantEnvironment => 'gray',
            TagBadgeDomain::Visibility => 'gray',
            TagBadgeDomain::SnapshotType => 'primary',
            TagBadgeDomain::BackupScheduleFrequency => 'info',
        };
    }

    /**
     * @return array<string, mixed>
     */
    private static function policyTypeMeta(string $type): array
    {
        if (self::$policyTypeMetaCache === []) {
            self::$policyTypeMetaCache = InventoryPolicyTypeMeta::byType();
        }

        return self::$policyTypeMetaCache[$type] ?? [];
    }

    /**
     * @return array<string, string>
     */
    private static function policyCategoryLabels(): array
    {
        if (self::$policyCategoryLabelCache !== []) {
            return self::$policyCategoryLabelCache;
        }

        $labels = [];

        foreach (InventoryPolicyTypeMeta::all() as $meta) {
            if (! is_array($meta)) {
                continue;
            }

            $category = $meta['category'] ?? null;
            if (! is_string($category) || trim($category) === '') {
                continue;
            }

            $normalized = Str::of($category)
                ->trim()
                ->lower()
                ->replaceMatches('/\s+/', ' ')
                ->toString();

            $labels[$normalized] ??= $category;
        }

        self::$policyCategoryLabelCache = $labels;

        return self::$policyCategoryLabelCache;
    }
}
