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

namespace Jovian.TagSystem.Editor {
    [CustomPropertyDrawer(typeof(JovianTagsGroup), true)]
    public class JovianTagsSelectionDrawer : PropertyDrawer {

        private const float RowHeight = 20f;
        private const float Spacing = 2f;
        private const float ButtonHeight = 20f;
        private const float FoldoutHeight = 20f;

        // Per-property foldout state keyed by property path
        private static readonly Dictionary<string, bool> FoldoutStates = new();

        private static bool GetFoldout(SerializedProperty property) {
            FoldoutStates.TryAdd(property.propertyPath, true);
            return FoldoutStates[property.propertyPath];
        }

        private static void SetFoldout(SerializedProperty property, bool value) {
            FoldoutStates[property.propertyPath] = value;
        }

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
            var tagsProp = property.FindPropertyRelative("tags");
            var count = tagsProp != null ? tagsProp.arraySize : 0;
            var expanded = GetFoldout(property);

            // Foldout row (always)
            var height = FoldoutHeight + Spacing;

            if(!expanded) return height;

            // Tag rows
            height += count * (RowHeight + Spacing);
            // Bottom button (always)
            height += ButtonHeight + Spacing;
            height += 2; // padding
            return height;
        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
            EditorGUI.BeginProperty(position, label, property);

            var tagsProp = property.FindPropertyRelative("tags");
            if(tagsProp == null) {
                EditorGUI.LabelField(position, label, new GUIContent("Error: 'tags' field not found"));
                EditorGUI.EndProperty();
                return;
            }

            // Auto-remove stale tags that no longer exist in any settings
            CleanStaleTags(tagsProp, property);

            var count = tagsProp.arraySize;
            var expanded = GetFoldout(property);

            // Foldout row with tag count
            var y = position.y;
            var foldoutRect = new Rect(position.x, y, position.width, FoldoutHeight);
            var foldoutLabel = count > 0 ? $"{label.text} ({count})" : label.text;
            var newExpanded = EditorGUI.Foldout(foldoutRect, expanded, foldoutLabel, true);
            if(newExpanded != expanded) SetFoldout(property, newExpanded);
            y += FoldoutHeight + Spacing;

            if(!newExpanded) {
                EditorGUI.EndProperty();
                return;
            }

            var indent = EditorGUI.indentLevel * 15f + 14f;
            var contentX = position.x + indent;
            var contentWidth = position.width - indent;

            // Tag rows
            for(int i = 0; i < tagsProp.arraySize; i++) {
                var elementProp = tagsProp.GetArrayElementAtIndex(i);
                var tagValue = elementProp.stringValue;

                var rowRect = new Rect(contentX, y, contentWidth, RowHeight);
                var tagRect = new Rect(rowRect.x + 4, rowRect.y, rowRect.width - 30, RowHeight);
                var removeBtnRect = new Rect(rowRect.xMax - 22, rowRect.y + 1, 20, RowHeight - 2);

                // Row background
                var bgColor = i % 2 == 0 ? new Color(0, 0, 0, 0.08f) : Color.clear;
                EditorGUI.DrawRect(rowRect, bgColor);

                // Tag label
                var displayName = string.IsNullOrEmpty(tagValue) ? "(empty)" : tagValue;
                EditorGUI.LabelField(tagRect, displayName, EditorStyles.label);

                // Remove button
                var prevBg = GUI.backgroundColor;
                GUI.backgroundColor = new Color(0.8f, 0.2f, 0.2f);
                if(GUI.Button(removeBtnRect, "✕", EditorStyles.miniButton)) {
                    tagsProp.DeleteArrayElementAtIndex(i);
                    property.serializedObject.ApplyModifiedProperties();
                    EditorGUI.EndProperty();
                    return;
                }
                GUI.backgroundColor = prevBg;

                y += RowHeight + Spacing;
            }

            // Bottom Select Tags button (always)
            DrawAddButton(contentX, y, contentWidth, tagsProp, property);

            EditorGUI.EndProperty();
        }

        private float DrawAddButton(float x, float y, float width, SerializedProperty tagsProp, SerializedProperty property) {
            var gap = 4f;
            var editBtnWidth = 70f;
            var addBtnRect = new Rect(x, y, width - editBtnWidth - gap, ButtonHeight);
            var editBtnRect = new Rect(x + width - editBtnWidth, y, editBtnWidth, ButtonHeight);

            // Edit Tags button — opens Tag Editor window
            if(GUI.Button(editBtnRect, "Edit Tags")) {
                JovianTagsEditorWindow.ShowWindow();
            }

            var prevBg = GUI.backgroundColor;
            GUI.backgroundColor = new Color(0.3f, 0.7f, 0.3f);
            if(GUI.Button(addBtnRect, "+ Select Tags")) {
                var currentTags = new HashSet<string>();
                for(int i = 0; i < tagsProp.arraySize; i++) {
                    var val = tagsProp.GetArrayElementAtIndex(i).stringValue;
                    if(!string.IsNullOrEmpty(val)) currentTags.Add(val);
                }

                JovianTagsPickerPopup.Show(currentTags, selectedTags => {
                    tagsProp.ClearArray();
                    foreach(var tag in selectedTags.OrderBy(t => t)) {
                        tagsProp.InsertArrayElementAtIndex(tagsProp.arraySize);
                        tagsProp.GetArrayElementAtIndex(tagsProp.arraySize - 1).stringValue = tag;
                    }
                    property.serializedObject.ApplyModifiedProperties();

                    foreach(var tag in selectedTags)
                        JovianTagsEditorHistory.Instance.AddRecentTag(tag);
                });
            }
            GUI.backgroundColor = prevBg;
            return y + ButtonHeight + Spacing;
        }
        private static HashSet<string> cachedValidTags;
        private static double lastCacheTime;

        private static void CleanStaleTags(SerializedProperty tagsProp, SerializedProperty property) {
            // Refresh valid tags cache every 2 seconds to avoid scanning settings every frame
            if(cachedValidTags == null || EditorApplication.timeSinceStartup - lastCacheTime > 2.0) {
                cachedValidTags = new HashSet<string>(System.StringComparer.Ordinal);
                var settings = JovianTagsEditorUtility.GetSettings();
                foreach(var s in settings)
                foreach(var t in s.gameTags)
                    if(!string.IsNullOrEmpty(t.tag))
                        cachedValidTags.Add(t.tag);
                lastCacheTime = EditorApplication.timeSinceStartup;
            }

            bool removed = false;
            for(int i = tagsProp.arraySize - 1; i >= 0; i--) {
                var val = tagsProp.GetArrayElementAtIndex(i).stringValue;
                if(!string.IsNullOrEmpty(val) && !cachedValidTags.Contains(val)) {
                    tagsProp.DeleteArrayElementAtIndex(i);
                    removed = true;
                }
            }

            if(removed) {
                property.serializedObject.ApplyModifiedProperties();
            }
        }
    }
}
