using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;

namespace Darkmatter.Libs.PlayerPrefs.Editor
{
    public class PlayerPrefsEditorWindow : EditorWindow
    {
        private PlayerPrefsKeyRegistry _registry;
        private Vector2 _scrollPos;
        private Dictionary<string, string> _editValues = new();
        private Dictionary<string, bool> _dirty = new();

        // Add-key fields
        private string _newKey = "";
        private PlayerPrefType _newType = PlayerPrefType.String;
        private string _newDescription = "";

        private enum Tab { Registered, Raw, Settings }
        private Tab _currentTab = Tab.Registered;

        [MenuItem("Tools/Darkmatter/PlayerPrefs Editor")]
        public static void Open()
        {
            var window = GetWindow<PlayerPrefsEditorWindow>("PlayerPrefs Editor");
            window.minSize = new Vector2(450, 300);
        }

        private void OnEnable()
        {
            FindRegistry();
        }

        private void FindRegistry()
        {
            var guids = AssetDatabase.FindAssets("t:PlayerPrefsKeyRegistry");
            if (guids.Length > 0)
            {
                var path = AssetDatabase.GUIDToAssetPath(guids[0]);
                _registry = AssetDatabase.LoadAssetAtPath<PlayerPrefsKeyRegistry>(path);
            }
        }

        private void OnGUI()
        {
            DrawToolbar();

            switch (_currentTab)
            {
                case Tab.Registered:
                    DrawRegisteredKeys();
                    break;
                case Tab.Raw:
                    DrawRawSection();
                    break;
                case Tab.Settings:
                    DrawSettings();
                    break;
            }
        }

        // ── Toolbar ─────────────────────────────────────────────

        private void DrawToolbar()
        {
            EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);

            if (GUILayout.Toggle(_currentTab == Tab.Registered, "Registered Keys", EditorStyles.toolbarButton))
                _currentTab = Tab.Registered;
            if (GUILayout.Toggle(_currentTab == Tab.Raw, "Raw Lookup", EditorStyles.toolbarButton))
                _currentTab = Tab.Raw;
            if (GUILayout.Toggle(_currentTab == Tab.Settings, "Settings", EditorStyles.toolbarButton))
                _currentTab = Tab.Settings;

            GUILayout.FlexibleSpace();

            GUI.backgroundColor = new Color(1f, 0.4f, 0.4f);
            if (GUILayout.Button("Delete All PlayerPrefs", EditorStyles.toolbarButton))
            {
                if (EditorUtility.DisplayDialog(
                    "Delete All PlayerPrefs",
                    "This will permanently delete ALL PlayerPrefs (both regular and protected). Are you sure?",
                    "Delete All", "Cancel"))
                {
                    UnityEngine.PlayerPrefs.DeleteAll();
                    UnityEngine.PlayerPrefs.Save();
                    _editValues.Clear();
                    _dirty.Clear();
                }
            }
            GUI.backgroundColor = Color.white;

            EditorGUILayout.EndHorizontal();
        }

        // ── Registered Keys Tab ─────────────────────────────────

        private void DrawRegisteredKeys()
        {
            // Registry selector
            EditorGUILayout.BeginHorizontal();
            _registry = (PlayerPrefsKeyRegistry)EditorGUILayout.ObjectField(
                "Key Registry", _registry, typeof(PlayerPrefsKeyRegistry), false);
            if (_registry == null && GUILayout.Button("Create", GUILayout.Width(60)))
            {
                CreateRegistry();
            }
            EditorGUILayout.EndHorizontal();

            if (_registry == null)
            {
                EditorGUILayout.HelpBox(
                    "Assign or create a PlayerPrefsKeyRegistry asset to manage keys.",
                    MessageType.Info);
                return;
            }

            EditorGUILayout.Space(4);

            // Add new key
            DrawAddKeySection();

            EditorGUILayout.Space(4);

            // Key list
            _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos);

            for (int i = _registry.Entries.Count - 1; i >= 0; i--)
            {
                var entry = _registry.Entries[i];
                DrawKeyEntry(entry, i);
            }

            EditorGUILayout.EndScrollView();
        }

        private void DrawAddKeySection()
        {
            EditorGUILayout.LabelField("Register New Key", EditorStyles.boldLabel);

            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            _newKey = EditorGUILayout.TextField("Key", _newKey);
            _newType = (PlayerPrefType)EditorGUILayout.EnumPopup("Type", _newType);
            _newDescription = EditorGUILayout.TextField("Description", _newDescription);

            EditorGUI.BeginDisabledGroup(string.IsNullOrWhiteSpace(_newKey));
            if (GUILayout.Button("Register Key"))
            {
                Undo.RecordObject(_registry, "Register PlayerPref Key");
                _registry.AddKey(_newKey, _newType, _newDescription);
                EditorUtility.SetDirty(_registry);
                _newKey = "";
                _newDescription = "";
                GenerateKeysClass(_generatedOutputPath);
            }
            EditorGUI.EndDisabledGroup();

            EditorGUILayout.EndVertical();
        }

        private void DrawKeyEntry(PlayerPrefKeyEntry entry, int index)
        {
            EditorGUILayout.BeginVertical(EditorStyles.helpBox);

            // Header row
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField(entry.Key, EditorStyles.boldLabel);
            GUILayout.Label($"[{entry.Type}]", GUILayout.Width(50));

            bool hasProtected = ProtectedPlayerPrefs.HasKey(entry.Key);
            bool hasRaw = UnityEngine.PlayerPrefs.HasKey(entry.Key);
            string source = hasProtected ? "Protected" : hasRaw ? "Raw" : "Not Set";
            GUILayout.Label(source, EditorStyles.miniLabel, GUILayout.Width(70));
            EditorGUILayout.EndHorizontal();

            if (!string.IsNullOrEmpty(entry.Description))
            {
                EditorGUILayout.LabelField(entry.Description, EditorStyles.miniLabel);
            }

            // Value display/edit
            string currentValue = GetCurrentValue(entry);
            string editKey = $"{entry.Key}_{index}";

            if (!_editValues.ContainsKey(editKey))
                _editValues[editKey] = currentValue;

            EditorGUILayout.BeginHorizontal();

            string newVal = entry.Type switch
            {
                PlayerPrefType.Bool => EditorGUILayout.Toggle("Value",
                    _editValues[editKey] == "1" || _editValues[editKey].Equals("true", StringComparison.OrdinalIgnoreCase))
                    ? "1" : "0",
                _ => EditorGUILayout.TextField("Value", _editValues[editKey])
            };

            if (newVal != _editValues[editKey])
            {
                _editValues[editKey] = newVal;
                _dirty[editKey] = true;
            }

            EditorGUILayout.EndHorizontal();

            // Action buttons
            EditorGUILayout.BeginHorizontal();

            EditorGUI.BeginDisabledGroup(!_dirty.ContainsKey(editKey) || !_dirty[editKey]);
            if (GUILayout.Button("Save", GUILayout.Width(50)))
            {
                SaveValue(entry, _editValues[editKey]);
                _dirty[editKey] = false;
                GenerateKeysClass(_generatedOutputPath);
            }
            EditorGUI.EndDisabledGroup();

            if (GUILayout.Button("Refresh", GUILayout.Width(60)))
            {
                _editValues[editKey] = GetCurrentValue(entry);
                _dirty[editKey] = false;
            }

            GUI.backgroundColor = new Color(1f, 0.6f, 0.4f);
            if (GUILayout.Button("Delete Value", GUILayout.Width(80)))
            {
                ProtectedPlayerPrefs.DeleteKey(entry.Key);
                UnityEngine.PlayerPrefs.DeleteKey(entry.Key);
                UnityEngine.PlayerPrefs.Save();
                _editValues[editKey] = "";
                _dirty[editKey] = false;
            }

            if (GUILayout.Button("Unregister", GUILayout.Width(80)))
            {
                Undo.RecordObject(_registry, "Unregister PlayerPref Key");
                _registry.RemoveKey(entry.Key);
                EditorUtility.SetDirty(_registry);
                _editValues.Remove(editKey);
                _dirty.Remove(editKey);
                GenerateKeysClass(_generatedOutputPath);
            }
            GUI.backgroundColor = Color.white;

            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
        }

        private string GetCurrentValue(PlayerPrefKeyEntry entry)
        {
            // Try protected first, then raw
            if (ProtectedPlayerPrefs.HasKey(entry.Key))
            {
                return entry.Type switch
                {
                    PlayerPrefType.String => ProtectedPlayerPrefs.GetString(entry.Key),
                    PlayerPrefType.Int => ProtectedPlayerPrefs.GetInt(entry.Key).ToString(),
                    PlayerPrefType.Float => ProtectedPlayerPrefs.GetFloat(entry.Key).ToString(),
                    PlayerPrefType.Bool => ProtectedPlayerPrefs.GetBool(entry.Key) ? "1" : "0",
                    _ => ""
                };
            }

            if (UnityEngine.PlayerPrefs.HasKey(entry.Key))
            {
                return entry.Type switch
                {
                    PlayerPrefType.String => UnityEngine.PlayerPrefs.GetString(entry.Key),
                    PlayerPrefType.Int => UnityEngine.PlayerPrefs.GetInt(entry.Key).ToString(),
                    PlayerPrefType.Float => UnityEngine.PlayerPrefs.GetFloat(entry.Key).ToString(),
                    PlayerPrefType.Bool => UnityEngine.PlayerPrefs.GetInt(entry.Key) == 1 ? "1" : "0",
                    _ => ""
                };
            }

            return "";
        }

        private void SaveValue(PlayerPrefKeyEntry entry, string value)
        {
            switch (entry.Type)
            {
                case PlayerPrefType.String:
                    ProtectedPlayerPrefs.SetString(entry.Key, value);
                    break;
                case PlayerPrefType.Int:
                    if (int.TryParse(value, out var intVal))
                        ProtectedPlayerPrefs.SetInt(entry.Key, intVal);
                    break;
                case PlayerPrefType.Float:
                    if (float.TryParse(value, out var floatVal))
                        ProtectedPlayerPrefs.SetFloat(entry.Key, floatVal);
                    break;
                case PlayerPrefType.Bool:
                    ProtectedPlayerPrefs.SetBool(entry.Key, value == "1");
                    break;
            }
            ProtectedPlayerPrefs.Save();
        }

        // ── Raw Lookup Tab ──────────────────────────────────────

        private string _rawKey = "";
        private string _rawValue = "";
        private string _rawType = "String";

        private void DrawRawSection()
        {
            EditorGUILayout.LabelField("Raw PlayerPrefs Lookup", EditorStyles.boldLabel);
            EditorGUILayout.HelpBox(
                "Look up any PlayerPrefs key directly (unprotected). Useful for debugging third-party or legacy keys.",
                MessageType.Info);

            _rawKey = EditorGUILayout.TextField("Key", _rawKey);
            _rawType = EditorGUILayout.TextField("Type (String/Int/Float)", _rawType);

            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("Get"))
            {
                _rawValue = _rawType.ToLower() switch
                {
                    "int" => UnityEngine.PlayerPrefs.GetInt(_rawKey).ToString(),
                    "float" => UnityEngine.PlayerPrefs.GetFloat(_rawKey).ToString(),
                    _ => UnityEngine.PlayerPrefs.GetString(_rawKey)
                };
            }

            if (GUILayout.Button("Has Key?"))
            {
                _rawValue = UnityEngine.PlayerPrefs.HasKey(_rawKey)
                    ? "Key EXISTS"
                    : "Key NOT found";
            }

            if (GUILayout.Button("Delete"))
            {
                UnityEngine.PlayerPrefs.DeleteKey(_rawKey);
                UnityEngine.PlayerPrefs.Save();
                _rawValue = "(deleted)";
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.Space(4);
            EditorGUILayout.LabelField("Result:", EditorStyles.boldLabel);
            EditorGUILayout.SelectableLabel(_rawValue, EditorStyles.textArea, GUILayout.MinHeight(40));
        }

        // ── Settings Tab ────────────────────────────────────────

        private string _generatedOutputPath = "Assets/Darkmatter/Code/Libs/PlayerPrefs/Runtime/PlayerPrefsKeys.cs";

        private void DrawSettings()
        {
            EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);

            EditorGUILayout.Space(8);

            EditorGUILayout.LabelField("Registry Asset", EditorStyles.boldLabel);
            _registry = (PlayerPrefsKeyRegistry)EditorGUILayout.ObjectField(
                "Active Registry", _registry, typeof(PlayerPrefsKeyRegistry), false);

            EditorGUILayout.Space(8);
            DrawProtectionSettings();

            EditorGUILayout.Space(8);
            EditorGUILayout.LabelField("Code Generation", EditorStyles.boldLabel);
            EditorGUILayout.HelpBox(
                "Generates a static PlayerPrefsKeys class so you can use PlayerPrefsKeys.Career.Level instead of string literals.",
                MessageType.Info);
            _generatedOutputPath = EditorGUILayout.TextField("Output Path", _generatedOutputPath);

            EditorGUI.BeginDisabledGroup(_registry == null || _registry.Entries.Count == 0);
            if (GUILayout.Button("Generate PlayerPrefsKeys.cs"))
            {
                GenerateKeysClass(_generatedOutputPath);
            }
            EditorGUI.EndDisabledGroup();

            EditorGUILayout.Space(8);
            EditorGUILayout.LabelField("Danger Zone", EditorStyles.boldLabel);

            GUI.backgroundColor = new Color(1f, 0.3f, 0.3f);
            if (GUILayout.Button("Delete ALL PlayerPrefs"))
            {
                if (EditorUtility.DisplayDialog(
                    "Delete All PlayerPrefs",
                    "This will permanently delete ALL PlayerPrefs. This cannot be undone.",
                    "Delete All", "Cancel"))
                {
                    UnityEngine.PlayerPrefs.DeleteAll();
                    UnityEngine.PlayerPrefs.Save();
                    _editValues.Clear();
                    _dirty.Clear();
                    Debug.Log("[PlayerPrefs Editor] All PlayerPrefs deleted.");
                }
            }
            GUI.backgroundColor = Color.white;
        }

        private void DrawProtectionSettings()
        {
            EditorGUILayout.LabelField("Protection Setup", EditorStyles.boldLabel);

            var settingsAsset = global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsSettingsUtility.FindSettingsAsset();
            using (new EditorGUI.DisabledScope(true))
            {
                EditorGUILayout.ObjectField(
                    "Settings Asset",
                    settingsAsset,
                    typeof(ProtectedPlayerPrefsSettings),
                    false);
            }

            var status = settingsAsset != null && settingsAsset.HasConfiguredHash
                ? "Configured"
                : "Missing";
            EditorGUILayout.LabelField("Hash Password", status);
            EditorGUILayout.LabelField("Runtime Source", ProtectedPlayerPrefs.InitializationSource);

            EditorGUILayout.BeginHorizontal();
            if (GUILayout.Button("Open Getting Started"))
            {
                global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsGettingStartedWindow.OpenWindow();
            }

            EditorGUI.BeginDisabledGroup(settingsAsset == null);
            if (GUILayout.Button("Ping Asset"))
            {
                EditorGUIUtility.PingObject(settingsAsset);
            }
            EditorGUI.EndDisabledGroup();

            if (GUILayout.Button("Open Docs"))
            {
                global::Darkmatter.Libs.PlayerPrefs.Editor.ProtectedPlayerPrefsSettingsUtility.OpenDocumentationPdf();
            }
            EditorGUILayout.EndHorizontal();
        }

        // ── Code Generation ─────────────────────────────────────

        private void GenerateKeysClass(string outputPath)
        {
            // Keys with a dot → nested class: "career.level" → PlayerPrefsKeys.Career.Level
            // Keys without a dot → flat const on PlayerPrefsKeys directly: "volume" → PlayerPrefsKeys.Volume
            var groups = new SortedDictionary<string, SortedDictionary<string, PlayerPrefKeyEntry>>();
            var flat = new SortedDictionary<string, PlayerPrefKeyEntry>();

            foreach (var entry in _registry.Entries)
            {
                var dotIndex = entry.Key.IndexOf('.');
                if (dotIndex > 0)
                {
                    var group = ToClassName(entry.Key.Substring(0, dotIndex));
                    var member = ToConstName(entry.Key.Substring(dotIndex + 1));
                    if (!groups.ContainsKey(group))
                        groups[group] = new SortedDictionary<string, PlayerPrefKeyEntry>();
                    groups[group][member] = entry;
                }
                else
                {
                    flat[ToConstName(entry.Key)] = entry;
                }
            }

            var sb = new StringBuilder();
            sb.AppendLine("// <auto-generated>");
            sb.AppendLine("// Generated by PlayerPrefs Editor — do not edit by hand.");
            sb.AppendLine("// Re-generate via Tools > Darkmatter > PlayerPrefs Editor > Settings.");
            sb.AppendLine("// </auto-generated>");
            sb.AppendLine();
            sb.AppendLine("namespace Darkmatter.Libs.PlayerPrefs");
            sb.AppendLine("{");
            sb.AppendLine("    public static class PlayerPrefsKeys");
            sb.AppendLine("    {");

            foreach (var (constName, entry) in flat)
            {
                if (!string.IsNullOrEmpty(entry.Description))
                    sb.AppendLine($"        /// <summary>{entry.Description} ({entry.Type})</summary>");
                sb.AppendLine($"        public const string {constName} = \"{entry.Key}\";");
            }

            if (flat.Count > 0 && groups.Count > 0)
                sb.AppendLine();

            foreach (var (groupName, members) in groups)
            {
                sb.AppendLine($"        public static class {groupName}");
                sb.AppendLine("        {");
                foreach (var (constName, entry) in members)
                {
                    if (!string.IsNullOrEmpty(entry.Description))
                        sb.AppendLine($"            /// <summary>{entry.Description} ({entry.Type})</summary>");
                    sb.AppendLine($"            public const string {constName} = \"{entry.Key}\";");
                }
                sb.AppendLine("        }");
                sb.AppendLine();
            }

            sb.AppendLine("    }");
            sb.AppendLine("}");

            var fullPath = Path.Combine(
                Path.GetDirectoryName(Application.dataPath),
                outputPath.TrimStart('/'));

            Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
            File.WriteAllText(fullPath, sb.ToString(), Encoding.UTF8);
            AssetDatabase.Refresh();

            Debug.Log($"[PlayerPrefs Editor] Generated {outputPath}");
        }

        private static string ToClassName(string segment)
        {
            var clean = Regex.Replace(segment, @"[^a-zA-Z0-9_]", "_");
            return char.ToUpper(clean[0]) + clean.Substring(1);
        }

        private static string ToConstName(string segment)
        {
            // "some.nested.key" → "SomeNestedKey"
            var parts = segment.Split('.');
            var sb = new StringBuilder();
            foreach (var part in parts)
            {
                var clean = Regex.Replace(part, @"[^a-zA-Z0-9_]", "_");
                if (clean.Length == 0) continue;
                sb.Append(char.ToUpper(clean[0]));
                sb.Append(clean.Substring(1));
            }
            return sb.ToString();
        }

        // ── Helpers ─────────────────────────────────────────────

        private void CreateRegistry()
        {
            var path = EditorUtility.SaveFilePanelInProject(
                "Create PlayerPrefs Key Registry",
                "PlayerPrefsKeyRegistry",
                "asset",
                "Choose where to save the registry asset.");

            if (string.IsNullOrEmpty(path)) return;

            var asset = CreateInstance<PlayerPrefsKeyRegistry>();
            AssetDatabase.CreateAsset(asset, path);
            AssetDatabase.SaveAssets();
            _registry = asset;
        }
    }
}
