// Asset Naming Manager — which rule set is active, plus per-user preferences.
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace AssetNamingManager
{
    /// <summary>
    /// Finds the rule set the project is using and remembers the small per-user choices
    /// (overlay on or off, last export folder) in <see cref="EditorPrefs"/>.
    /// Keys are namespaced per project so two checkouts on one machine do not fight.
    /// </summary>
    public static class NamingManagerSettings
    {
        /// <summary>Where <see cref="CreateRuleSet"/> puts a new rule set.</summary>
        public const string DefaultRuleSetFolder = "Assets/AssetNamingManager/Settings";

        /// <summary>File name of a freshly created rule set.</summary>
        public const string DefaultRuleSetName = "NamingRules.asset";

        static string _prefix;
        static NamingRuleSet _cached;

        static string Prefix
        {
            get
            {
                if (_prefix == null)
                {
                    // Application.dataPath is unique per checkout, which is exactly the scope we want.
                    int hash = Application.dataPath.GetHashCode();
                    _prefix = "AssetNamingManager." + hash.ToString("X8") + ".";
                }
                return _prefix;
            }
        }

        // ---------------------------------------------------------------- active rule set

        /// <summary>GUID of the rule set the project is using. Empty means "auto-discover".</summary>
        public static string ActiveRuleSetGuid
        {
            get { return EditorPrefs.GetString(Prefix + "ruleSet", string.Empty); }
            set
            {
                EditorPrefs.SetString(Prefix + "ruleSet", value ?? string.Empty);
                _cached = null;
            }
        }

        /// <summary>
        /// The rule set to scan with. Uses the explicitly chosen one when it still exists,
        /// otherwise the first <see cref="NamingRuleSet"/> found in the project. Returns null
        /// when the project has none yet.
        /// </summary>
        public static NamingRuleSet ActiveRuleSet
        {
            get
            {
                if (_cached != null) return _cached;

                string guid = ActiveRuleSetGuid;
                if (!string.IsNullOrEmpty(guid))
                {
                    string path = AssetDatabase.GUIDToAssetPath(guid);
                    if (!string.IsNullOrEmpty(path))
                    {
                        _cached = AssetDatabase.LoadAssetAtPath<NamingRuleSet>(path);
                        if (_cached != null) return _cached;
                    }
                }

                // Hold the result in a local: assigning ActiveRuleSetGuid clears _cached by design,
                // so writing the pref first and caching second is the only order that survives.
                var found = FindFirstInProject();
                if (found != null) ActiveRuleSetGuid = GuidOf(found);
                _cached = found;
                return _cached;
            }
            set
            {
                ActiveRuleSetGuid = value != null ? GuidOf(value) : string.Empty;
                _cached = value;
            }
        }

        /// <summary>Drops the cached reference. Called after an import or a rule set edit.</summary>
        public static void InvalidateCache()
        {
            _cached = null;
        }

        // With Fast Enter Play Mode - the default for new Unity 6.6 projects - the domain is
        // not reloaded, so statics survive between Play sessions. Clear the cache here so a
        // session never starts holding something the previous one left behind.
        [InitializeOnEnterPlayMode]
        static void ResetOnEnterPlayMode()
        {
            InvalidateCache();
        }

        /// <summary>Every rule set asset in the project, for the picker dropdown.</summary>
        public static List<NamingRuleSet> FindAll()
        {
            var list = new List<NamingRuleSet>();
            foreach (var guid in AssetDatabase.FindAssets("t:" + typeof(NamingRuleSet).Name))
            {
                string path = AssetDatabase.GUIDToAssetPath(guid);
                var asset = AssetDatabase.LoadAssetAtPath<NamingRuleSet>(path);
                if (asset != null) list.Add(asset);
            }
            list.Sort((a, b) => string.Compare(a.name, b.name, StringComparison.Ordinal));
            return list;
        }

        static NamingRuleSet FindFirstInProject()
        {
            var all = FindAll();
            return all.Count > 0 ? all[0] : null;
        }

        static string GuidOf(NamingRuleSet asset)
        {
            string path = AssetDatabase.GetAssetPath(asset);
            return string.IsNullOrEmpty(path) ? string.Empty : AssetDatabase.AssetPathToGUID(path);
        }

        /// <summary>
        /// Creates a rule set asset seeded with <paramref name="preset"/>, selects it and makes it active.
        /// </summary>
        public static NamingRuleSet CreateRuleSet(NamingPreset preset, string folder = DefaultRuleSetFolder)
        {
            if (!NamingFixer.EnsureFolder(folder))
            {
                UnityEngine.Debug.LogError("[Asset Naming Manager] Could not create the folder " + folder);
                return null;
            }

            var asset = ScriptableObject.CreateInstance<NamingRuleSet>();
            NamingRulePresets.Apply(asset, preset);

            string path = AssetDatabase.GenerateUniqueAssetPath(folder + "/" + DefaultRuleSetName);
            AssetDatabase.CreateAsset(asset, path);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            ActiveRuleSet = asset;
            return asset;
        }

        // ---------------------------------------------------------------- user preferences

        /// <summary>Draw a coloured dot on offending assets in the Project window.</summary>
        public static bool ShowProjectOverlay
        {
            get { return EditorPrefs.GetBool(Prefix + "overlay", true); }
            set { EditorPrefs.SetBool(Prefix + "overlay", value); }
        }

        /// <summary>Run a scan automatically the first time the window is opened in a session.</summary>
        public static bool ScanOnOpen
        {
            get { return EditorPrefs.GetBool(Prefix + "scanOnOpen", true); }
            set { EditorPrefs.SetBool(Prefix + "scanOnOpen", value); }
        }

        /// <summary>Also print every finding to the console after a scan.</summary>
        public static bool LogToConsole
        {
            get { return EditorPrefs.GetBool(Prefix + "logConsole", false); }
            set { EditorPrefs.SetBool(Prefix + "logConsole", value); }
        }

        /// <summary>Ask before applying a batch of fixes. Strongly recommended.</summary>
        public static bool ConfirmBeforeFixing
        {
            get { return EditorPrefs.GetBool(Prefix + "confirmFix", true); }
            set { EditorPrefs.SetBool(Prefix + "confirmFix", value); }
        }

        /// <summary>
        /// GUID of the override stylesheet layered on top of the base look. Empty means the
        /// built-in Unity-grey default.
        /// </summary>
        public static string ActiveThemeGuid
        {
            get { return EditorPrefs.GetString(Prefix + "theme", string.Empty); }
            set { EditorPrefs.SetString(Prefix + "theme", value ?? string.Empty); }
        }

        /// <summary>Last folder used in the export dialog.</summary>
        public static string LastExportFolder
        {
            get { return EditorPrefs.GetString(Prefix + "exportFolder", string.Empty); }
            set { EditorPrefs.SetString(Prefix + "exportFolder", value ?? string.Empty); }
        }

        /// <summary>Remembered grouping mode for the report list.</summary>
        public static GroupMode GroupMode
        {
            get { return (GroupMode)EditorPrefs.GetInt(Prefix + "group", (int)GroupMode.Rule); }
            set { EditorPrefs.SetInt(Prefix + "group", (int)value); }
        }

        /// <summary>Severity filter bitmask: 1 = Info, 2 = Warning, 4 = Error.</summary>
        public static int SeverityMask
        {
            get { return EditorPrefs.GetInt(Prefix + "sevMask", 7); }
            set { EditorPrefs.SetInt(Prefix + "sevMask", value); }
        }

        /// <summary>Resets every per-user preference. Rule set assets are left alone.</summary>
        public static void ResetPreferences()
        {
            EditorPrefs.DeleteKey(Prefix + "overlay");
            EditorPrefs.DeleteKey(Prefix + "scanOnOpen");
            EditorPrefs.DeleteKey(Prefix + "logConsole");
            EditorPrefs.DeleteKey(Prefix + "confirmFix");
            EditorPrefs.DeleteKey(Prefix + "exportFolder");
            EditorPrefs.DeleteKey(Prefix + "theme");
            EditorPrefs.DeleteKey(Prefix + "group");
            EditorPrefs.DeleteKey(Prefix + "sevMask");
        }
    }
}
