using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using Meezi.Core.Branding;

namespace Meezi.Infrastructure.Branding;

public static partial class CafeThemeSerializer
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
        PropertyNameCaseInsensitive = true
    };

    private static readonly HashSet<string> ValidPalettes =
    [
        "meezi-green", "ocean-blue", "royal-purple", "sunset-orange", "rose-blush",
        "charcoal-gold", "espresso", "forest", "midnight", "coral", "gold-luxury",
        "mint-fresh", "wine-bar", "slate-modern", "cherry", "teal-wave", "sand-cafe"
    ];

    private static readonly HashSet<string> ValidPanelStyles =
    [
        CafeThemeDefaults.PanelFlat, CafeThemeDefaults.PanelModern, CafeThemeDefaults.PanelGlass,
        CafeThemeDefaults.PanelMinimal, CafeThemeDefaults.PanelBold, CafeThemeDefaults.PanelSoft,
        CafeThemeDefaults.PanelElevated, CafeThemeDefaults.PanelOutline
    ];

    private static readonly HashSet<string> ValidMenuStyles =
    [
        CafeThemeDefaults.MenuCards, CafeThemeDefaults.MenuCompact, CafeThemeDefaults.MenuGrid,
        CafeThemeDefaults.MenuList, CafeThemeDefaults.MenuMagazine, CafeThemeDefaults.MenuClassic
    ];

    private static readonly HashSet<string> ValidMenuTextures =
    [
        CafeThemeDefaults.MenuTextureNone, CafeThemeDefaults.MenuTexturePaper,
        CafeThemeDefaults.MenuTextureLinen, CafeThemeDefaults.MenuTextureDots,
        CafeThemeDefaults.MenuTextureGrid, CafeThemeDefaults.MenuTextureMarble,
        CafeThemeDefaults.MenuTextureWood, CafeThemeDefaults.MenuTextureWarm
    ];

    private static readonly HashSet<string> ValidDensities =
    [
        CafeThemeDefaults.DensityCompact, CafeThemeDefaults.DensityComfortable, CafeThemeDefaults.DensitySpacious
    ];

    private static readonly HashSet<string> ValidRadius =
    [
        CafeThemeDefaults.RadiusNone, CafeThemeDefaults.RadiusSm, CafeThemeDefaults.RadiusMd,
        CafeThemeDefaults.RadiusLg, CafeThemeDefaults.RadiusFull
    ];

    public static CafeTheme Parse(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
            return new CafeTheme();

        try
        {
            var theme = JsonSerializer.Deserialize<CafeTheme>(json, JsonOptions) ?? new CafeTheme();
            return Normalize(theme);
        }
        catch
        {
            return new CafeTheme();
        }
    }

    public static string Serialize(CafeTheme theme) =>
        JsonSerializer.Serialize(Normalize(theme), JsonOptions);

    public static CafeTheme Normalize(CafeTheme theme)
    {
        theme.PaletteId = ValidPalettes.Contains(theme.PaletteId) ? theme.PaletteId : CafeThemeDefaults.PaletteMeeziGreen;
        theme.PanelStyle = ValidPanelStyles.Contains(theme.PanelStyle) ? theme.PanelStyle : CafeThemeDefaults.PanelModern;
        theme.MenuStyle = ValidMenuStyles.Contains(theme.MenuStyle) ? theme.MenuStyle : CafeThemeDefaults.MenuCards;
        theme.MenuTexture = ValidMenuTextures.Contains(theme.MenuTexture)
            ? theme.MenuTexture
            : CafeThemeDefaults.MenuTextureNone;
        theme.Density = ValidDensities.Contains(theme.Density) ? theme.Density : CafeThemeDefaults.DensityComfortable;
        theme.Radius = ValidRadius.Contains(theme.Radius) ? theme.Radius : CafeThemeDefaults.RadiusMd;
        theme.Custom = NormalizeCustom(theme.Custom);
        return theme;
    }

    private static CafeThemeCustomColors? NormalizeCustom(CafeThemeCustomColors? custom)
    {
        if (custom is null) return null;

        var normalized = new CafeThemeCustomColors
        {
            Primary = NormalizeHex(custom.Primary),
            Secondary = NormalizeHex(custom.Secondary),
            Accent = NormalizeHex(custom.Accent),
            Background = NormalizeHex(custom.Background),
            Surface = NormalizeHex(custom.Surface),
            Text = NormalizeHex(custom.Text),
            TextMuted = NormalizeHex(custom.TextMuted),
            Destructive = NormalizeHex(custom.Destructive),
            Success = NormalizeHex(custom.Success),
            PrimaryOpacity = NormalizeOpacity(custom.PrimaryOpacity),
            SecondaryOpacity = NormalizeOpacity(custom.SecondaryOpacity),
            AccentOpacity = NormalizeOpacity(custom.AccentOpacity),
            BackgroundOpacity = NormalizeOpacity(custom.BackgroundOpacity),
            SurfaceOpacity = NormalizeOpacity(custom.SurfaceOpacity),
            TextOpacity = NormalizeOpacity(custom.TextOpacity),
            TextMutedOpacity = NormalizeOpacity(custom.TextMutedOpacity),
            DestructiveOpacity = NormalizeOpacity(custom.DestructiveOpacity),
            SuccessOpacity = NormalizeOpacity(custom.SuccessOpacity)
        };

        return normalized.Primary is null
            && normalized.Secondary is null
            && normalized.Accent is null
            && normalized.Background is null
            && normalized.Surface is null
            && normalized.Text is null
            && normalized.TextMuted is null
            && normalized.Destructive is null
            && normalized.Success is null
            && normalized.PrimaryOpacity is null
            && normalized.SecondaryOpacity is null
            && normalized.AccentOpacity is null
            && normalized.BackgroundOpacity is null
            && normalized.SurfaceOpacity is null
            && normalized.TextOpacity is null
            && normalized.TextMutedOpacity is null
            && normalized.DestructiveOpacity is null
            && normalized.SuccessOpacity is null
            ? null
            : normalized;
    }

    private static string? NormalizeHex(string? value)
    {
        if (string.IsNullOrWhiteSpace(value)) return null;
        var v = value.Trim();
        if (!HexColorRegex().IsMatch(v)) return null;
        return v.StartsWith('#') ? v.ToUpperInvariant() : $"#{v.ToUpperInvariant()}";
    }

    private static int? NormalizeOpacity(int? value)
    {
        if (value is null) return null;
        return Math.Clamp(value.Value, 0, 100);
    }

    [GeneratedRegex(@"^#?[0-9A-Fa-f]{6}$", RegexOptions.Compiled)]
    private static partial Regex HexColorRegex();
}
