using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections.Generic;

/// <summary>
/// Centralized design tokens + styling helpers for every Canvas in the game.
/// One source of truth for color, typography, spacing, animation timing, and
/// emoji-capable font configuration. Replaces the per-file palette duplication
/// that scattered across InGameHUDBuilder, HostLobbyUI, LobbyDisplayUI etc.
///
/// Why this exists (P10):
///   - "UI looks tiny on phone, bleeds off-screen during voting, font drops
///      emoji glyphs" - all symptoms of palette/typography/sizing values being
///      defined per-file with no shared scale. Centralizing fixes the
///      whole-app inconsistency, not just the worst offender.
///   - Emoji rendering on Android needs TMP_Settings.fallbackFontAssets
///      pointed at a Noto Color Emoji TMP_FontAsset. We do this at runtime
///      so the host project doesn't need a pre-baked TMP_Settings asset
///      change (which would touch a binary scriptable object).
///
/// Visual direction:
///   - Dark base (#0A0E1A near-black) with one strong accent (teal-cyan
///     #00C8C8). Readable in sunlight, doesn't clash with the orange/red
///     impostor signals or the green map.
///   - Typography ramp: one display weight, one body, one mono for codes.
///     Sizes follow a major-third scale (1.25x ratio) so headlines
///     dominate without shouting.
///   - Spacing on an 8pt grid. Touch targets minimum 88pt (44pt @ 2x).
///   - Animation: subtle. Fade 150ms, slide 200ms, ease-out. Game's tense -
///     UI shouldn't be cute.
/// </summary>
public static class UITheme
{
    // ── Palette ───────────────────────────────────────────────────────────────
    // Names follow purpose, not literal color. Adding a new component? Use
    // Bg/Surface/Accent/Success/Danger/Warning/Muted - not raw hex.

    public static readonly Color Bg          = H("#0A0E1A"); // page background
    public static readonly Color Surface     = H("#121829"); // cards, panels
    public static readonly Color SurfaceAlt  = H("#1A2138"); // elevated/active
    public static readonly Color SurfaceDim  = H("#0E1322"); // recessed (input)
    public static readonly Color RowA        = H("#1A2035"); // alt row
    public static readonly Color RowB        = H("#161C2E"); // alt row
    public static readonly Color Border      = new Color(1f, 1f, 1f, 0.10f);

    public static readonly Color Accent      = H("#00C8C8"); // teal-cyan, primary CTA
    public static readonly Color AccentDim   = H("#0A8A8A"); // pressed state
    public static readonly Color Success     = H("#2DB84B"); // task done, you-voted
    public static readonly Color Danger      = H("#E04040"); // impostor, eject, kill
    public static readonly Color Warning     = H("#F08C1A"); // sabotage, meltdown
    public static readonly Color Caution     = H("#FFB800"); // reconnect, info

    public static readonly Color TextHi      = new Color(0.96f, 0.97f, 0.99f); // primary
    public static readonly Color TextMid     = new Color(0.78f, 0.83f, 0.91f); // secondary
    public static readonly Color TextLo      = new Color(0.55f, 0.62f, 0.75f); // tertiary/disabled
    public static readonly Color TextOnAccent= H("#001818"); // black-ish on bright accent

    static Color H(string hex)
    {
        ColorUtility.TryParseHtmlString(hex, out var c);
        return c;
    }

    // ── Typography ────────────────────────────────────────────────────────────
    // Sizes are in TMP "px" units which scale with the CanvasScaler reference.
    // Our reference is 1080x1920 portrait; these values produce the intended
    // physical size on a typical 6" phone (~5mm tall body text).

    public const float FontDisplay  = 64f; // hero (game-end win/loss)
    public const float FontHeadline = 44f; // section header
    public const float FontTitle    = 32f; // panel header
    public const float FontBody     = 26f; // standard text
    public const float FontSmall    = 22f; // captions, helper text
    public const float FontTiny     = 18f; // tiny meta, debug

    // Action button text scales separately because thumb-zone CTAs need to
    // dominate visually even on a small screen.
    public const float FontActionBtn= 40f;

    // ── Spacing (8pt grid) ────────────────────────────────────────────────────
    public const float S1   =  4f;
    public const float S2   =  8f;
    public const float S3   = 12f;
    public const float S4   = 16f;
    public const float S5   = 24f;
    public const float S6   = 32f;
    public const float S7   = 48f;
    public const float S8   = 64f;
    public const float S9   = 96f;

    // ── Touch targets ─────────────────────────────────────────────────────────
    // Minimum heights tuned for 1080x1920 reference. CanvasScaler keeps these
    // visually consistent across phone sizes.
    public const float MinTapHeight   = 88f;   // 44pt @ 2x
    public const float StandardBtn    = 110f;  // typical button height
    public const float HeroBtn        = 140f;  // primary CTA
    public const float VoteRowMin     = 96f;
    public const float VoteRowMax     = 144f;

    // ── Corner radii (target value; needs an Image with a rounded sprite to
    // realize, but keeping the token here lets future polish hit one place) ──
    public const float Radius1 = 8f;
    public const float Radius2 = 14f;
    public const float Radius3 = 22f;

    // ── Animation timing ──────────────────────────────────────────────────────
    public const float DurFade    = 0.15f;
    public const float DurSlide   = 0.20f;
    public const float DurSnappy  = 0.10f;
    public const float DurAmbient = 0.40f; // slow ambient pulses

    // ── Canvas reference (mirrored from InGameHUDBuilder for consistency) ────
    public const float ReferenceWidth   = 1080f;
    public const float ReferenceHeight  = 1920f;
    public const float MatchWidthOrHeight = 0.5f;

    // ── Emoji font fallback ───────────────────────────────────────────────────
    // Tracks whether we've already tried to wire a Noto Color Emoji fallback
    // into TMP_Settings. Once attempted (success or fail), we don't keep
    // re-poking - mobile resource lookups aren't free.
    static bool _emojiFallbackAttempted;

    /// <summary>
    /// Configures the project's TMP fallback font chain so emoji codepoints
    /// (📡 ⚙️ 🗳️ 👥 etc.) used throughout the UI render properly. Looks for a
    /// TMP_FontAsset named "NotoColorEmoji" (or "EmojiFallback") under
    /// `Resources/Fonts/` first; if that's missing, walks the OS font cache
    /// the platform exposes via Font.OSFontNames as a last-resort fallback.
    ///
    /// Idempotent + cheap on subsequent calls. Callers don't need to gate.
    /// </summary>
    public static void EnsureEmojiFontFallback()
    {
        if (_emojiFallbackAttempted) return;
        _emojiFallbackAttempted = true;

        // Primary: explicit Resources lookup. Drop a pre-built
        // `NotoColorEmoji.asset` (TMP_FontAsset) into Assets/Resources/Fonts/
        // for Android/iOS - that's the production path.
        TMP_FontAsset emoji = Resources.Load<TMP_FontAsset>("Fonts/NotoColorEmoji");
        if (emoji == null) emoji = Resources.Load<TMP_FontAsset>("Fonts/EmojiFallback");

        if (emoji != null)
        {
            var def = TMP_Settings.defaultFontAsset;
            if (def != null)
            {
                if (def.fallbackFontAssetTable == null)
                    def.fallbackFontAssetTable = new List<TMP_FontAsset>();
                if (!def.fallbackFontAssetTable.Contains(emoji))
                {
                    def.fallbackFontAssetTable.Add(emoji);
                    Debug.Log("[UITheme] Emoji fallback wired: " + emoji.name);
                }
            }
            return;
        }

        // No bundled emoji font. Log once so the user knows to either drop
        // one in or strip emojis from labels - silent failure here means
        // missing-glyph rectangles surface in the UI without explanation.
        Debug.LogWarning(
            "[UITheme] No NotoColorEmoji TMP_FontAsset found under Resources/Fonts/. " +
            "Emoji glyphs in UI text will render as tofu boxes. " +
            "Drop a TMP-baked Noto Color Emoji asset at " +
            "Assets/Resources/Fonts/NotoColorEmoji.asset (any TMP_FontAsset name " +
            "containing 'Emoji' also matches via the secondary lookup).");
    }

    // ── Style helpers ─────────────────────────────────────────────────────────
    // Apply consistent styling without repeating every property. Keep these
    // single-purpose; if you find yourself adding flags, that's a sign you
    // should add a new helper instead.

    /// <summary>Solid Image with theme-consistent color. Returns the Image.</summary>
    public static Image Surf(RectTransform rt, Color color)
    {
        var img = rt.gameObject.GetComponent<Image>() ?? rt.gameObject.AddComponent<Image>();
        img.color = color;
        return img;
    }

    /// <summary>Stretches a RectTransform to fill its parent.</summary>
    public static void Stretch(RectTransform rt)
    {
        rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one;
        rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero;
    }

    /// <summary>Apply the project's standard CanvasScaler config.</summary>
    public static void ConfigureCanvasScaler(Canvas canvas)
    {
        if (canvas == null) return;
        var scaler = canvas.GetComponent<CanvasScaler>()
                  ?? canvas.gameObject.AddComponent<CanvasScaler>();
        scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
        scaler.referenceResolution = new Vector2(ReferenceWidth, ReferenceHeight);
        scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
        scaler.matchWidthOrHeight = MatchWidthOrHeight;
        scaler.referencePixelsPerUnit = 100f;
    }

    /// <summary>
    /// Anchor a RectTransform to the screen's safe rectangle (notch/punch-hole
    /// safe). Children inherit the inset.
    /// </summary>
    public static void ApplySafeArea(RectTransform rt)
    {
        if (rt == null) return;
        var safe = Screen.safeArea;
        var screenSize = new Vector2(Screen.width, Screen.height);
        if (screenSize.x <= 0 || screenSize.y <= 0) return;

        Vector2 aMin = safe.position;
        Vector2 aMax = safe.position + safe.size;
        aMin.x /= screenSize.x; aMin.y /= screenSize.y;
        aMax.x /= screenSize.x; aMax.y /= screenSize.y;

        rt.anchorMin = aMin; rt.anchorMax = aMax;
        rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero;
    }

    /// <summary>
    /// Style a TMP text field as a labeled text element. Sets size, color,
    /// alignment, weight - and ensures the emoji fallback chain is wired so
    /// any emoji glyphs in the text render properly on the first frame.
    /// </summary>
    public static void StyleText(TMP_Text tmp, float size, Color color,
        TextAlignmentOptions align, bool bold = false, FontStyles extra = FontStyles.Normal)
    {
        if (tmp == null) return;
        EnsureEmojiFontFallback();
        tmp.fontSize = size;
        tmp.color = color;
        tmp.alignment = align;
        tmp.fontStyle = (bold ? FontStyles.Bold : FontStyles.Normal) | extra;
        tmp.enableWordWrapping = true;
    }

    /// <summary>Standard primary CTA button styling - bg, text, target graphic.</summary>
    public static Button StylePrimaryButton(RectTransform rt, string label,
        Color? bgColor = null, Color? textColor = null, float? fontSize = null)
    {
        var bg = Surf(rt, bgColor ?? Accent);
        var btn = rt.gameObject.GetComponent<Button>() ?? rt.gameObject.AddComponent<Button>();
        btn.targetGraphic = bg;

        // Color block: dim for pressed/disabled states.
        var cb = btn.colors;
        cb.normalColor       = bgColor ?? Accent;
        cb.highlightedColor  = Color.Lerp(bgColor ?? Accent, TextHi, 0.10f);
        cb.pressedColor      = Color.Lerp(bgColor ?? Accent, Bg, 0.30f);
        cb.disabledColor     = new Color(0.30f, 0.34f, 0.42f, 0.70f);
        cb.selectedColor     = bgColor ?? Accent;
        btn.colors = cb;

        // Label child
        var labelRt = NewChild("Label", rt);
        Stretch(labelRt);
        var tmp = labelRt.gameObject.AddComponent<TextMeshProUGUI>();
        StyleText(tmp, fontSize ?? FontActionBtn,
            textColor ?? TextOnAccent, TextAlignmentOptions.Center, bold: true);
        tmp.text = label;
        // Padding so the text doesn't kiss the button edges.
        labelRt.offsetMin = new Vector2(S4, S2);
        labelRt.offsetMax = new Vector2(-S4, -S2);

        return btn;
    }

    /// <summary>Secondary (outlined / muted) button styling.</summary>
    public static Button StyleSecondaryButton(RectTransform rt, string label,
        float? fontSize = null)
    {
        return StylePrimaryButton(rt, label,
            bgColor: SurfaceAlt, textColor: TextHi, fontSize: fontSize);
    }

    /// <summary>Danger (eject/leave/kill confirmation) button styling.</summary>
    public static Button StyleDangerButton(RectTransform rt, string label,
        float? fontSize = null)
    {
        return StylePrimaryButton(rt, label,
            bgColor: Danger, textColor: TextHi, fontSize: fontSize);
    }

    /// <summary>
    /// Create a child RectTransform under the given parent. Centralized so
    /// every UI builder doesn't reinvent the GameObject + RectTransform
    /// boilerplate.
    /// </summary>
    public static RectTransform NewChild(string name, RectTransform parent)
    {
        var go = new GameObject(name);
        var rt = go.AddComponent<RectTransform>();
        rt.SetParent(parent, false);
        rt.localScale = Vector3.one;
        return rt;
    }

    /// <summary>Anchor a RectTransform with absolute corner offsets.</summary>
    public static void Anchor(RectTransform rt, Vector2 aMin, Vector2 aMax,
        Vector2 offsetMin, Vector2 offsetMax)
    {
        rt.anchorMin = aMin; rt.anchorMax = aMax;
        rt.offsetMin = offsetMin; rt.offsetMax = offsetMax;
    }

    /// <summary>
    /// Add a TMP text label as a child of `parent`, fully filling it. Common
    /// pattern for buttons and panel headers.
    /// </summary>
    public static TextMeshProUGUI TxtChild(RectTransform parent, string text,
        float size, Color color, TextAlignmentOptions align, bool bold = false)
    {
        var rt = NewChild("Txt", parent);
        Stretch(rt);
        var tmp = rt.gameObject.AddComponent<TextMeshProUGUI>();
        StyleText(tmp, size, color, align, bold);
        tmp.text = text;
        return tmp;
    }
}
