using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace Jovian.TagSystem.Editor {

    public class JovianTagsPickerPopup : EditorWindow {

        private static readonly Color DividerColor = new(1f, 1f, 1f, 0.12f);
        private static readonly Color RowOdd = new(1f, 1f, 1f, 0.03f);
        private static readonly Color RowHover = new(0.3f, 0.5f, 0.8f, 0.12f);
        private static readonly Color HeaderBg = new(0.18f, 0.18f, 0.18f, 1f);
        private static readonly Color ChildIndentLine = new(1f, 1f, 1f, 0.08f);

        private const int RowHeight = 22;
        private const int DepthIndent = 20;

        private HashSet<string> selectedTags = new();
        private Action<HashSet<string>> onConfirm;
        private string searchFilter = "";
        private Vector2 scrollPosition;
        private List<string> allTags = new();
        private readonly Dictionary<string, bool> foldoutState = new();
        private int rowIndex;

        private GUIStyle _tagStyle;

        private GUIStyle TagStyle => _tagStyle ??= new GUIStyle(EditorStyles.label) {
            fontSize = 12, fixedHeight = RowHeight
        };

        private GUIStyle _headerStyle;

        private GUIStyle HeaderStyle => _headerStyle ??= new GUIStyle(EditorStyles.boldLabel) {
            fontSize = 12, fixedHeight = 24
        };

        private class PickerNode {
            public string Name;
            public string FullPath;
            public List<PickerNode> Children = new();
        }

        public static void Show(HashSet<string> currentTags, Action<HashSet<string>> onConfirm) {
            var window = CreateInstance<JovianTagsPickerPopup>();
            window.selectedTags = new HashSet<string>(currentTags);
            window.onConfirm = onConfirm;
            window.titleContent = new GUIContent("Select Tags");
            window.RefreshTags();

            var size = new Vector2(380, 440);
            var mousePos = GUIUtility.GUIToScreenPoint(Event.current.mousePosition);
            window.position = new Rect(mousePos.x - size.x * 0.5f, mousePos.y, size.x, size.y);
            window.minSize = new Vector2(300, 300);
            window.ShowUtility();
        }

        private void RefreshTags() {
            allTags.Clear();
            var settings = JovianTagsEditorUtility.GetSettings();
            var tagSet = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach(var s in settings)
            foreach(var t in s.gameTags)
                if(!string.IsNullOrEmpty(t.tag))
                    tagSet.Add(t.tag);
            allTags = tagSet.ToList();
        }

        private void OnGUI() {
            EditorGUILayout.BeginVertical();
            GUILayout.Space(6);

            // Search
            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(8);
            GUILayout.Label("Search:", GUILayout.Width(50));
            searchFilter = EditorGUILayout.TextField(searchFilter);
            if(!string.IsNullOrEmpty(searchFilter) && GUILayout.Button("✕", GUILayout.Width(20), GUILayout.Height(18))) {
                searchFilter = "";
                GUI.FocusControl(null);
            }
            GUILayout.Space(8);
            EditorGUILayout.EndHorizontal();

            GUILayout.Space(4);

            // Selection count
            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(8);
            GUILayout.Label($"{selectedTags.Count} selected", EditorStyles.centeredGreyMiniLabel);
            GUILayout.FlexibleSpace();
            if(selectedTags.Count > 0 && GUILayout.Button("Clear All", EditorStyles.miniButton, GUILayout.Width(60)))
                selectedTags.Clear();
            GUILayout.Space(8);
            EditorGUILayout.EndHorizontal();

            GUILayout.Space(2);
            DrawDivider();

            // Tree
            scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
            DrawTagTree();
            EditorGUILayout.EndScrollView();

            DrawDivider();
            GUILayout.Space(4);

            // Buttons
            EditorGUILayout.BeginHorizontal();
            GUILayout.Space(8);
            var prevBg = GUI.backgroundColor;
            GUI.backgroundColor = new Color(0.3f, 0.8f, 0.3f);
            if(GUILayout.Button("Confirm", GUILayout.Height(26))) {
                onConfirm?.Invoke(selectedTags);
                Close();
            }
            GUI.backgroundColor = prevBg;
            if(GUILayout.Button("Cancel", GUILayout.Height(26)))
                Close();
            if(GUILayout.Button("Edit Tags", GUILayout.Height(26), GUILayout.Width(70)))
                JovianTagsEditorWindow.ShowWindow();
            GUILayout.Space(8);
            EditorGUILayout.EndHorizontal();

            GUILayout.Space(6);
            EditorGUILayout.EndVertical();
        }

        private void DrawTagTree() {
            if(allTags.Count == 0) {
                GUILayout.Space(20);
                GUILayout.Label("No tags defined.", EditorStyles.centeredGreyMiniLabel);
                return;
            }

            var roots = BuildTree();

            // Filter
            if(!string.IsNullOrEmpty(searchFilter)) {
                roots = FilterTree(roots, searchFilter);
            }

            rowIndex = 0;
            foreach(var root in roots.OrderBy(r => r.Name, StringComparer.OrdinalIgnoreCase)) {
                DrawPickerNode(root, 0);
            }
        }

        private void DrawPickerNode(PickerNode node, int depth) {
            var hasChildren = node.Children.Count > 0;
            var isSelected = selectedTags.Contains(node.FullPath);

            var bgColor = rowIndex % 2 != 0 ? RowOdd : Color.clear;
            var rect = EditorGUILayout.BeginHorizontal(GUILayout.Height(RowHeight));
            if(rect.Contains(Event.current.mousePosition)) bgColor = RowHover;
            if(depth == 0) bgColor = HeaderBg;
            if(bgColor.a > 0) EditorGUI.DrawRect(rect, bgColor);

            // Indent + guide lines
            GUILayout.Space(6 + depth * DepthIndent);
            if(depth > 0 && Event.current.type == EventType.Repaint) {
                for(int d = 1; d <= depth; d++) {
                    var lineX = 6 + d * DepthIndent - 10;
                    EditorGUI.DrawRect(new Rect(lineX, rect.y, 1, rect.height), ChildIndentLine);
                }
            }

            // Checkbox
            var newSelected = EditorGUILayout.Toggle(isSelected, GUILayout.Width(16), GUILayout.Height(RowHeight));
            if(newSelected != isSelected) {
                if(newSelected) selectedTags.Add(node.FullPath);
                else selectedTags.Remove(node.FullPath);
            }

            // Foldout for parents
            if(hasChildren) {
                foldoutState.TryAdd(node.FullPath, true);
                foldoutState[node.FullPath] = EditorGUILayout.Foldout(
                    foldoutState[node.FullPath], "", true, EditorStyles.foldout);
                GUILayout.Space(-4);
            }
            else {
                GUILayout.Space(14);
            }

            // Label
            var style = depth == 0 ? HeaderStyle : TagStyle;
            GUILayout.Label(node.Name, style, GUILayout.ExpandWidth(true));

            // Select all children button
            if(hasChildren) {
                var allChildTags = CollectAllPaths(node);
                var allChildrenSelected = allChildTags.All(t => selectedTags.Contains(t));
                var prevColor = GUI.color;
                GUI.color = new Color(1, 1, 1, 0.5f);
                if(GUILayout.Button(allChildrenSelected ? "−all" : "+all", EditorStyles.miniButton,
                    GUILayout.Width(32), GUILayout.Height(16))) {
                    foreach(var t in allChildTags) {
                        if(allChildrenSelected) selectedTags.Remove(t);
                        else selectedTags.Add(t);
                    }
                }
                GUI.color = prevColor;
            }

            GUILayout.Space(4);
            EditorGUILayout.EndHorizontal();
            rowIndex++;

            // Children
            if(!hasChildren) return;
            foldoutState.TryAdd(node.FullPath, true);
            if(!foldoutState[node.FullPath]) return;

            foreach(var child in node.Children.OrderBy(c => c.Name, StringComparer.OrdinalIgnoreCase)) {
                DrawPickerNode(child, depth + 1);
            }
        }

        // ===== Tree helpers =====

        private List<PickerNode> BuildTree() {
            var rootNodes = new Dictionary<string, PickerNode>();
            var allNodes = new Dictionary<string, PickerNode>();

            foreach(var tag in allTags) {
                var parts = tag.Split(JovianTagsHandler.tagDelimiter);
                PickerNode parent = null;
                var accumulated = "";

                for(int i = 0; i < parts.Length; i++) {
                    accumulated = i == 0 ? parts[i] : accumulated + "." + parts[i];
                    if(!allNodes.TryGetValue(accumulated, out var node)) {
                        node = new PickerNode { Name = parts[i], FullPath = accumulated };
                        allNodes[accumulated] = node;
                        if(parent != null) parent.Children.Add(node);
                        else rootNodes[accumulated] = node;
                    }
                    parent = node;
                }
            }
            return rootNodes.Values.ToList();
        }

        private List<PickerNode> FilterTree(List<PickerNode> roots, string filter) {
            var result = new List<PickerNode>();
            foreach(var root in roots) {
                var filtered = FilterNode(root, filter);
                if(filtered != null) result.Add(filtered);
            }
            return result;
        }

        private PickerNode FilterNode(PickerNode node, string filter) {
            var matchesSelf = node.FullPath.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0;
            var filteredChildren = new List<PickerNode>();
            foreach(var child in node.Children) {
                var fc = FilterNode(child, filter);
                if(fc != null) filteredChildren.Add(fc);
            }
            if(!matchesSelf && filteredChildren.Count == 0) return null;
            return new PickerNode {
                Name = node.Name,
                FullPath = node.FullPath,
                Children = matchesSelf ? node.Children : filteredChildren
            };
        }

        private List<string> CollectAllPaths(PickerNode node) {
            var result = new List<string>();
            CollectPathsRecursive(node, result);
            return result;
        }

        private void CollectPathsRecursive(PickerNode node, List<string> result) {
            result.Add(node.FullPath);
            foreach(var child in node.Children) CollectPathsRecursive(child, result);
        }

        private static void DrawDivider() {
            var rect = EditorGUILayout.GetControlRect(false, 1);
            EditorGUI.DrawRect(rect, new Color(1f, 1f, 1f, 0.12f));
        }
    }
}
