using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Overlays;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UIElements;

namespace Gyvr.Mythril2D
{
    [Overlay(typeof(SceneView), "Mythril2D Scene Selector", true)]
    public class SceneSelectorOverlay : Overlay
    {
        ScrollView _list;
        Label _navLabel;
        string _search = "";
        string _tagFilter = "";
        bool _settings;
        HashSet<string> _collapsed = new();

        public SceneSelectorOverlay()
        {
            EditorApplication.projectChanged += () => { if (displayed) { displayed = false; displayed = true; } };
        }

        public override VisualElement CreatePanelContent()
        {
            var root = new VisualElement();
            root.style.minWidth = 260;
            root.style.maxWidth = 300;
            root.style.backgroundColor = new Color(0.12f, 0.12f, 0.14f, 0.75f);
            root.style.SetBorderRadius(6);
            root.style.paddingTop = 4;
            root.style.paddingBottom = 4;

            BuildHeader(root);
            _list = new ScrollView(ScrollViewMode.Vertical) { style = { maxHeight = 380 } };
            Rebuild();
            root.Add(_list);
            return root;
        }

        void BuildHeader(VisualElement root)
        {
            var row = new VisualElement { style = { flexDirection = FlexDirection.Row, marginBottom = 4, paddingLeft = 4, paddingRight = 4, alignItems = Align.Center } };

            // Search field container with icon
            var searchContainer = new VisualElement { style = { flexDirection = FlexDirection.Row, flexGrow = 1, marginRight = 4, alignItems = Align.Center } };
            var searchIcon = new Image { image = EditorGUIUtility.IconContent("d_Search Icon").image, style = { width = 14, height = 14, marginRight = 2 }, tintColor = new Color(0.5f, 0.5f, 0.5f) };
            searchContainer.Add(searchIcon);
            var txt = new TextField { style = { flexGrow = 1, minHeight = 22 } };
            txt.RegisterValueChangedCallback(e => { _search = e.newValue; Rebuild(); });
            searchContainer.Add(txt);
            row.Add(searchContainer);

            var tags = new List<string> { "All" };
            tags.AddRange(SceneMetadataManager.Asset.availableTags);
            var tagDrop = new DropdownField(tags, 0) { style = { minWidth = 70, minHeight = 22 } };
            tagDrop.RegisterValueChangedCallback(e => { _tagFilter = e.newValue == "All" ? "" : e.newValue; Rebuild(); });
            row.Add(tagDrop);

            _navLabel = new Label(_settings ? "←" : "⚙") { style = { fontSize = 13, width = 22, minHeight = 22, unityTextAlign = TextAnchor.MiddleCenter, color = new Color(0.55f, 0.55f, 0.55f) } };
            _navLabel.RegisterCallback<ClickEvent>(e => { _settings = !_settings; Rebuild(); });
            _navLabel.RegisterCallback<MouseEnterEvent>(e => _navLabel.style.color = Color.white);
            _navLabel.RegisterCallback<MouseLeaveEvent>(e => _navLabel.style.color = new Color(0.55f, 0.55f, 0.55f));
            row.Add(_navLabel);

            root.Add(row);
        }

        void Rebuild()
        {
            _list.Clear();
            if (_navLabel != null) _navLabel.text = _settings ? "←" : "⚙";
            if (_settings) { BuildSettings(); return; }

            var all = SceneUtil.GetAllScenesInAssetDatabase().ToList();
            if (!string.IsNullOrEmpty(_search))
                all = all.Where(s => Path.GetFileNameWithoutExtension(s).ToLower().Contains(_search.ToLower())).ToList();
            if (!string.IsNullOrEmpty(_tagFilter))
                all = all.Where(s => SceneMetadataManager.HasTag(s, _tagFilter)).ToList();

            var inBuild = all.Where(SceneUtil.IsSceneInBuildSettings).ToList();
            var notInBuild = all.Except(inBuild).ToList();
            var favs = inBuild.Where(SceneMetadataManager.IsFavorite).OrderBy(Path.GetFileNameWithoutExtension).ToList();
            var rest = inBuild.Except(favs).ToList();

            if (favs.Any())
            {
                AddHeader("★ Favorites", "fav", new Color(1f, 0.75f, 0.2f), false);
                if (!_collapsed.Contains("fav"))
                    foreach (var s in favs) AddSceneRow(s);
            }

            var byCat = rest.GroupBy(s => SceneMetadataManager.GetCategory(s) ?? "").OrderBy(g => string.IsNullOrEmpty(g.Key) ? "~" : g.Key);
            foreach (var cat in byCat)
            {
                string catKey = "cat_" + (cat.Key ?? "uncategorized");
                string catName = string.IsNullOrEmpty(cat.Key) ? "Uncategorized" : cat.Key;
                AddHeader(catName, catKey, new Color(0.55f, 0.7f, 0.85f));

                if (_collapsed.Contains(catKey)) continue;

                var bySub = cat.GroupBy(s => SceneMetadataManager.GetSubcategory(s) ?? "").OrderBy(g => string.IsNullOrEmpty(g.Key) ? "~" : g.Key);
                foreach (var sub in bySub)
                {
                    if (!string.IsNullOrEmpty(sub.Key))
                    {
                        string subKey = catKey + "_" + sub.Key;
                        AddSubHeader(sub.Key, subKey);
                        if (!_collapsed.Contains(subKey))
                            foreach (var s in sub.OrderBy(Path.GetFileNameWithoutExtension)) AddSceneRow(s, false, 24);
                    }
                    else
                    {
                        foreach (var s in sub.OrderBy(Path.GetFileNameWithoutExtension)) AddSceneRow(s, false, 12);
                    }
                }
            }

            if (notInBuild.Any())
            {
                AddHeader("⚠ Not In Build", "notinbuild", new Color(0.85f, 0.5f, 0.35f), false);
                if (!_collapsed.Contains("notinbuild"))
                    foreach (var s in notInBuild.OrderBy(Path.GetFileNameWithoutExtension)) AddSceneRow(s, true);
            }
        }

        void AddHeader(string text, string key, Color c, bool folder = true)
        {
            bool collapsed = _collapsed.Contains(key);
            var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginTop = 8, marginBottom = 3, marginLeft = 4, paddingBottom = 3, borderBottomWidth = 1, borderBottomColor = new Color(c.r, c.g, c.b, 0.5f) } };

            var arrow = new Label(collapsed ? "▸" : "▾") { style = { fontSize = 10, width = 12, color = c, unityTextAlign = TextAnchor.MiddleCenter } };
            row.Add(arrow);

            if (folder) row.Add(new Label("📁") { style = { fontSize = 11, marginRight = 4 } });

            var lbl = new Label(text) { style = { unityFontStyleAndWeight = FontStyle.Bold, fontSize = 12, color = c, flexGrow = 1 } };
            row.Add(lbl);

            row.RegisterCallback<ClickEvent>(e => { if (collapsed) _collapsed.Remove(key); else _collapsed.Add(key); Rebuild(); });
            row.RegisterCallback<MouseEnterEvent>(e => row.style.backgroundColor = new Color(1, 1, 1, 0.03f));
            row.RegisterCallback<MouseLeaveEvent>(e => row.style.backgroundColor = Color.clear);
            _list.Add(row);
        }

        void AddSubHeader(string text, string key)
        {
            bool collapsed = _collapsed.Contains(key);
            var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, marginTop = 4, marginLeft = 16, paddingTop = 2, paddingBottom = 2 } };

            var arrow = new Label(collapsed ? "▸" : "▾") { style = { fontSize = 9, width = 10, color = new Color(0.55f, 0.7f, 0.55f), unityTextAlign = TextAnchor.MiddleCenter } };
            row.Add(arrow);
            row.Add(new Label("🏷") { style = { fontSize = 9, marginRight = 3 } });

            var lbl = new Label(text) { style = { fontSize = 11, color = new Color(0.55f, 0.7f, 0.55f) } };
            row.Add(lbl);

            row.RegisterCallback<ClickEvent>(e => { if (collapsed) _collapsed.Remove(key); else _collapsed.Add(key); Rebuild(); });
            row.RegisterCallback<MouseEnterEvent>(e => row.style.backgroundColor = new Color(1, 1, 1, 0.03f));
            row.RegisterCallback<MouseLeaveEvent>(e => row.style.backgroundColor = Color.clear);
            _list.Add(row);
        }

        void AddSceneRow(string path, bool dim = false, int indent = 6)
        {
            bool current = UnityEngine.SceneManagement.SceneManager.GetActiveScene().path == path;
            var row = new VisualElement { style = { flexDirection = FlexDirection.Row, alignItems = Align.Center, paddingLeft = indent, paddingRight = 4, paddingTop = 1, paddingBottom = 1 } };
            if (current) row.style.backgroundColor = new Color(1f, 0.7f, 0.3f, 0.15f);
            row.RegisterCallback<MouseEnterEvent>(e => row.style.backgroundColor = current ? new Color(1f, 0.7f, 0.3f, 0.25f) : new Color(1, 1, 1, 0.04f));
            row.RegisterCallback<MouseLeaveEvent>(e => row.style.backgroundColor = current ? new Color(1f, 0.7f, 0.3f, 0.15f) : Color.clear);

            bool fav = SceneMetadataManager.IsFavorite(path);
            var star = new Label(fav ? "★" : "☆") { style = { fontSize = 12, width = 14, color = fav ? new Color(1f, 0.75f, 0.15f) : new Color(0.35f, 0.35f, 0.35f), unityTextAlign = TextAnchor.MiddleCenter } };
            star.RegisterCallback<ClickEvent>(e => { SceneMetadataManager.SetFavorite(path, !fav); Rebuild(); e.StopPropagation(); });
            star.RegisterCallback<MouseEnterEvent>(e => { if (!fav) star.style.color = new Color(0.65f, 0.65f, 0.65f); });
            star.RegisterCallback<MouseLeaveEvent>(e => star.style.color = fav ? new Color(1f, 0.75f, 0.15f) : new Color(0.35f, 0.35f, 0.35f));
            row.Add(star);

            var clr = current ? new Color(1f, 0.85f, 0.5f) : (dim ? new Color(0.45f, 0.45f, 0.45f) : new Color(0.8f, 0.8f, 0.8f));
            var name = new Label(Path.GetFileNameWithoutExtension(path)) { style = { flexGrow = 1, fontSize = 12, color = clr, overflow = Overflow.Hidden, textOverflow = TextOverflow.Ellipsis }, tooltip = path };
            name.RegisterCallback<ClickEvent>(e => { if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) EditorSceneManager.OpenScene(path); });
            name.RegisterCallback<MouseEnterEvent>(e => name.style.color = new Color(0.5f, 0.75f, 1f));
            name.RegisterCallback<MouseLeaveEvent>(e => name.style.color = clr);
            row.Add(name);

            var tl = SceneMetadataManager.GetTags(path);
            if (tl.Any())
            {
                row.Add(new Label(string.Join(" ", tl.Take(2).Select(GetTagIcon))) { style = { fontSize = 11, color = new Color(0.55f, 0.55f, 0.55f), marginRight = 4 }, tooltip = string.Join(", ", tl) });
            }

            var dots = new Image { image = EditorGUIUtility.IconContent("_Menu").image, style = { width = 16, height = 16 }, tintColor = new Color(0.5f, 0.5f, 0.5f) };
            dots.RegisterCallback<ClickEvent>(e => { ShowContextMenu(path); e.StopPropagation(); });
            dots.RegisterCallback<MouseEnterEvent>(e => dots.tintColor = Color.white);
            dots.RegisterCallback<MouseLeaveEvent>(e => dots.tintColor = new Color(0.5f, 0.5f, 0.5f));
            row.Add(dots);

            _list.Add(row);
        }

        string GetTagIcon(string tag)
        {
            return tag.ToLower() switch
            {
                "wip" => "⚡",
                "complete" => "✓",
                "needs review" => "?",
                "important" => "!",
                "playtest" => "▶",
                _ => tag.Length > 2 ? tag[..2] : tag
            };
        }

        void ShowContextMenu(string path)
        {
            var menu = new GenericMenu();
            var cat = SceneMetadataManager.GetCategory(path);
            var sub = SceneMetadataManager.GetSubcategory(path);
            var tags = SceneMetadataManager.GetTags(path);

            menu.AddItem(new GUIContent("Category/None"), string.IsNullOrEmpty(cat), () => { SceneMetadataManager.SetCategory(path, ""); Rebuild(); });
            foreach (var c in SceneMetadataManager.Asset.availableCategories)
                menu.AddItem(new GUIContent("Category/" + c), cat == c, () => { SceneMetadataManager.SetCategory(path, c); Rebuild(); });

            menu.AddItem(new GUIContent("Type/None"), string.IsNullOrEmpty(sub), () => { SceneMetadataManager.SetSubcategory(path, ""); Rebuild(); });
            foreach (var s in SceneMetadataManager.Asset.availableSubcategories)
                menu.AddItem(new GUIContent("Type/" + s), sub == s, () => { SceneMetadataManager.SetSubcategory(path, s); Rebuild(); });

            foreach (var t in SceneMetadataManager.Asset.availableTags)
                menu.AddItem(new GUIContent("Tags/" + t), tags.Contains(t), () => { SceneMetadataManager.ToggleTag(path, t); Rebuild(); });

            menu.ShowAsContext();
        }

        void BuildSettings()
        {
            var hdr = new Label("Settings") { style = { unityFontStyleAndWeight = FontStyle.Bold, fontSize = 12, marginBottom = 6, marginLeft = 4 } };
            _list.Add(hdr);

            AddEditableList("Categories", SceneMetadataManager.Asset.availableCategories, SceneMetadataManager.AddCategory, SceneMetadataManager.RemoveCategory);
            AddEditableList("Types", SceneMetadataManager.Asset.availableSubcategories, SceneMetadataManager.AddSubcategory, SceneMetadataManager.RemoveSubcategory);
            AddEditableList("Tags", SceneMetadataManager.Asset.availableTags, SceneMetadataManager.AddAvailableTag, SceneMetadataManager.RemoveAvailableTag);
        }

        void AddEditableList(string title, List<string> items, Action<string> add, Action<string> remove)
        {
            var sec = new VisualElement { style = { marginBottom = 8, marginLeft = 4 } };

            var lbl = new Label(title) { style = { fontSize = 11, color = new Color(0.6f, 0.6f, 0.6f), marginBottom = 2 } };
            sec.Add(lbl);

            foreach (var item in items.ToList())
            {
                var r = new VisualElement { style = { flexDirection = FlexDirection.Row, marginLeft = 6 } };
                r.Add(new Label("• " + item) { style = { flexGrow = 1, fontSize = 12 } });

                var x = new Label("×") { style = { fontSize = 11, width = 14, color = new Color(0.55f, 0.35f, 0.35f), unityTextAlign = TextAnchor.MiddleCenter } };
                x.RegisterCallback<ClickEvent>(e => { remove(item); Rebuild(); });
                x.RegisterCallback<MouseEnterEvent>(e => x.style.color = new Color(1f, 0.4f, 0.4f));
                x.RegisterCallback<MouseLeaveEvent>(e => x.style.color = new Color(0.55f, 0.35f, 0.35f));
                r.Add(x);
                sec.Add(r);
            }

            var ar = new VisualElement { style = { flexDirection = FlexDirection.Row, marginLeft = 6, marginTop = 2 } };
            var tf = new TextField { style = { flexGrow = 1, height = 16 } };
            ar.Add(tf);

            var btn = new Label("+") { style = { fontSize = 12, width = 16, color = new Color(0.4f, 0.55f, 0.4f), unityTextAlign = TextAnchor.MiddleCenter } };
            btn.RegisterCallback<ClickEvent>(e => { if (!string.IsNullOrWhiteSpace(tf.value)) { add(tf.value.Trim()); tf.value = ""; Rebuild(); } });
            btn.RegisterCallback<MouseEnterEvent>(e => btn.style.color = new Color(0.5f, 0.85f, 0.5f));
            btn.RegisterCallback<MouseLeaveEvent>(e => btn.style.color = new Color(0.4f, 0.55f, 0.4f));
            ar.Add(btn);

            sec.Add(ar);
            _list.Add(sec);
        }
    }

    static class StyleExtensions
    {
        public static void SetBorderRadius(this IStyle style, float r)
        {
            style.borderTopLeftRadius = r;
            style.borderTopRightRadius = r;
            style.borderBottomLeftRadius = r;
            style.borderBottomRightRadius = r;
        }
    }
}
