// Asset Naming Manager — colours and icons, in one place so the window stays coherent.
using UnityEditor;
using UnityEngine;

namespace AssetNamingManager.UI
{
    /// <summary>
    /// The palette. Everything that draws a colour asks here, so switching Unity between the Pro
    /// and Light skins never leaves half the window unreadable.
    /// </summary>
    /// <remarks>
    /// These values mirror <c>Editor/UI/Styles/NamingManager.uss</c> exactly. The greys are Unity's own
    /// editor greys, so the window sits flush beside a docked Inspector. USS paints the chrome,
    /// this class paints the Painter2D charts, and the two sit six pixels apart on the Dashboard —
    /// if they drift, one severity shows up in two different reds. Change one, change the other.
    ///
    /// Three roles per severity, and they are not interchangeable:
    /// <list type="bullet">
    /// <item><description><b>Fill</b> — pills, stripes, chart arcs. Graphics, so 3:1 is the bar.</description></item>
    /// <item><description><b>Ink</b> — text. Measured at or above 4.5:1 against the panel in its own skin.</description></item>
    /// <item><description><b>Soft</b> — tints behind chips and armed rows.</description></item>
    /// </list>
    /// Every ratio in the comments below is WCAG 2.1, computed rather than eyeballed.
    /// </remarks>
    public static class NamingManagerTheme
    {
        static bool Dark { get { return EditorGUIUtility.isProSkin; } }

        static Color Rgb(int r, int g, int b)
        {
            return new Color(r / 255f, g / 255f, b / 255f);
        }

        static Color Rgba(int r, int g, int b, float a)
        {
            return new Color(r / 255f, g / 255f, b / 255f, a);
        }

        // ---------------------------------------------------------------- surfaces

        /// <summary>Window background.</summary>
        public static Color Background
        {
            get { return Dark ? Rgb(48, 48, 48) : Rgb(190, 190, 190); }
        }

        /// <summary>Card and panel background — the surface every contrast ratio here is measured against.</summary>
        public static Color Panel
        {
            get { return Dark ? Rgb(56, 56, 56) : Rgb(200, 200, 200); }
        }

        /// <summary>Hairline colour.</summary>
        public static Color Line
        {
            get { return Dark ? Rgb(35, 35, 35) : Rgb(153, 153, 153); }
        }

        /// <summary>Primary text.</summary>
        public static Color Text
        {
            get { return Dark ? Rgb(210, 210, 210) : Rgb(9, 9, 9); }
        }

        /// <summary>Secondary text. 4.79:1 dark, 5.29:1 light.</summary>
        public static Color Muted
        {
            get { return Dark ? Rgb(166, 166, 166) : Rgb(72, 72, 72); }
        }

        /// <summary>
        /// Deliberately low-contrast text, for things that are switched OFF. 3.55:1 dark, 3.90:1 light —
        /// under the body-text bar on purpose, because "inactive" should read as inactive.
        /// </summary>
        public static Color Dim
        {
            get { return Dark ? Rgb(142, 142, 142) : Rgb(94, 94, 94); }
        }

        /// <summary>Low-contrast colour for tracks, dividers and empty chart segments.</summary>
        public static Color Track
        {
            get { return Dark ? new Color(1f, 1f, 1f, 0.12f) : new Color(0f, 0f, 0f, 0.14f); }
        }

        // ---------------------------------------------------------------- brand

        /// <summary>Brand colour: badge, primary button, chart fills.</summary>
        public static Color Accent
        {
            get { return Dark ? Rgb(58, 121, 187) : Rgb(58, 114, 176); }
        }

        /// <summary>Accent as text. 4.55:1 dark, 4.53:1 light.</summary>
        public static Color AccentInk
        {
            get { return Dark ? Rgb(123, 165, 209) : Rgb(44, 86, 132); }
        }

        /// <summary>Accent tint behind an armed row or the selection bar.</summary>
        public static Color AccentSoft
        {
            get { return Dark ? Rgba(58, 121, 187, 0.30f) : Rgba(58, 114, 176, 0.22f); }
        }

        // ---------------------------------------------------------------- severity fills

        /// <summary>Fill for <see cref="NamingSeverity.Error"/>.</summary>
        public static Color Error
        {
            get { return Dark ? Rgb(230, 92, 92) : Rgb(161, 35, 35); }
        }

        /// <summary>
        /// Fill for <see cref="NamingSeverity.Warning"/>. The light value is far darker than the
        /// dark one on purpose: bright amber is close to invisible on Unity's light grey panel.
        /// </summary>
        public static Color Warning
        {
            get { return Dark ? Rgb(242, 176, 46) : Rgb(111, 77, 9); }
        }

        /// <summary>Fill for <see cref="NamingSeverity.Info"/>.</summary>
        public static Color Info
        {
            get { return Dark ? Rgb(90, 150, 234) : Rgb(40, 84, 148); }
        }

        /// <summary>Fill for "all clear" states and for corrected names.</summary>
        public static Color Good
        {
            get { return Dark ? Rgb(68, 191, 131) : Rgb(28, 94, 65); }
        }

        // ---------------------------------------------------------------- severity inks

        /// <summary>Error as text. 4.55:1 dark, 4.51:1 light.</summary>
        public static Color ErrorInk
        {
            get { return Dark ? Rgb(236, 131, 131) : Rgb(161, 35, 35); }
        }

        /// <summary>Warning as text. 6.16:1 dark, 4.58:1 light.</summary>
        public static Color WarningInk
        {
            get { return Dark ? Rgb(242, 176, 46) : Rgb(111, 77, 9); }
        }

        /// <summary>Info as text. 4.54:1 dark, 4.51:1 light.</summary>
        public static Color InfoInk
        {
            get { return Dark ? Rgb(110, 163, 237) : Rgb(40, 84, 148); }
        }

        /// <summary>Good as text. 5.04:1 dark, 4.60:1 light. This is the corrected-name green.</summary>
        public static Color GoodInk
        {
            get { return Dark ? Rgb(68, 191, 131) : Rgb(28, 94, 65); }
        }

        // ---------------------------------------------------------------- lookups

        /// <summary>The fill colour for a severity — stripes, pills, chart arcs.</summary>
        public static Color For(NamingSeverity severity)
        {
            switch (severity)
            {
                case NamingSeverity.Error:   return Error;
                case NamingSeverity.Warning: return Warning;
                default:                     return Info;
            }
        }

        /// <summary>The text colour for a severity. Use this whenever the colour carries words.</summary>
        public static Color InkFor(NamingSeverity severity)
        {
            switch (severity)
            {
                case NamingSeverity.Error:   return ErrorInk;
                case NamingSeverity.Warning: return WarningInk;
                default:                     return InfoInk;
            }
        }

        /// <summary>USS class suffix for a severity: <c>error</c>, <c>warning</c>, <c>info</c>.</summary>
        public static string ClassFor(NamingSeverity severity)
        {
            return severity.ToString().ToLowerInvariant();
        }

        /// <summary>
        /// Colour for a health score. The thresholds match <see cref="ScanReport.HealthLabel"/>
        /// so the number never turns amber while the caption still reads "Healthy".
        /// </summary>
        public static Color ForScore(float score)
        {
            if (score >= 95f) return Good;      // "Spotless" / "Healthy"
            if (score >= 70f) return Warning;   // "Minor drift" / "Needs a pass"
            return Error;                       // "Messy" / "Critical"
        }

        /// <summary>Distinct-ish colours for chart series, cycling safely for long lists.</summary>
        public static Color Series(int index)
        {
            Color[] palette =
            {
                Accent,
                Dark ? Rgb(77, 181, 209) : Rgb(29, 124, 150),
                Good,
                Warning,
                Dark ? Rgb(186, 140, 220) : Rgb(104, 72, 148),
                Error,
                Info,
                Dark ? Rgb(158, 184, 92) : Rgb(96, 118, 40)
            };
            return palette[((index % palette.Length) + palette.Length) % palette.Length];
        }

        // ---------------------------------------------------------------- rich text

        /// <summary>
        /// A colour as <c>RRGGBB</c>, for Unity rich-text tags. The Report row highlights only the
        /// characters that actually change, which needs one Label with markup rather than three
        /// Labels that each ellipsize separately.
        /// </summary>
        public static string ToHex(Color color)
        {
            return ColorUtility.ToHtmlStringRGB(color);
        }

        /// <summary>
        /// Builds <c>old <b>new</b></c> markup where the changed tail of the name is bold and green,
        /// so the eye reads the transformation without a legend.
        /// Returns plain text when the two names share nothing, which is the honest rendering.
        /// </summary>
        public static string HighlightDiff(string before, string after)
        {
            if (string.IsNullOrEmpty(after)) return before ?? string.Empty;
            if (string.IsNullOrEmpty(before)) return after;

            int shared = NameUtility.CommonPrefixLength(before, after);
            string hex = ToHex(GoodInk);

            if (shared <= 0)
                return "<b><color=#" + hex + ">" + after + "</color></b>";

            return after.Substring(0, shared) +
                   "<b><color=#" + hex + ">" + after.Substring(shared) + "</color></b>";
        }

        // ---------------------------------------------------------------- icons

        /// <summary>The small built-in console icon matching a severity.</summary>
        public static Texture SeverityIcon(NamingSeverity severity)
        {
            switch (severity)
            {
                case NamingSeverity.Error:   return EditorGUIUtility.IconContent("console.erroricon.sml").image;
                case NamingSeverity.Warning: return EditorGUIUtility.IconContent("console.warnicon.sml").image;
                default:                     return EditorGUIUtility.IconContent("console.infoicon.sml").image;
            }
        }

        /// <summary>Thumbnail for an asset path, falling back to a generic icon.</summary>
        public static Texture AssetIcon(string assetPath)
        {
            var icon = AssetDatabase.GetCachedIcon(assetPath);
            if (icon != null) return icon;
            return EditorGUIUtility.IconContent("DefaultAsset Icon").image;
        }

        /// <summary>A built-in icon by name, or null when this Unity version does not ship it.</summary>
        public static Texture Icon(string name)
        {
            var content = EditorGUIUtility.IconContent(name);
            return content != null ? content.image : null;
        }
    }
}
