using System;
using System.Collections.Generic;
using System.Linq;
using AlicizaX.UI.Extension;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.UI;
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;

namespace UnityEngine.UI
{
    [CanEditMultipleObjects]
    [CustomEditor(typeof(UXSelectable), true)]
    internal class UXSelectableEditor : Editor
    {
        private static bool s_ShowNavigation = false;
        public static string s_ShowNavigationKey = "SelectableEditor.ShowNavigation";
        GUIContent m_VisualizeNavigation = EditorGUIUtility.TrTextContent("Visualize", "Show navigation flows between selectable UI elements.");
        private static List<UXSelectableEditor> s_Editors = new List<UXSelectableEditor>();

        // 序列化属性（基类需要）
        protected SerializedProperty m_Navigation;
        protected SerializedProperty m_MainTransition;
        protected SerializedProperty m_Interactable;
        protected SerializedProperty m_animator;

        // 抽象皮肤（现在放在基类）
        protected GUISkin customSkin;

        // Tab 管理结构
        protected class EditorTab
        {
            public string title;
            public string iconName;
            public List<Action> callbacks = new List<Action>();

            public EditorTab(string t, string icon)
            {
                title = t;
                iconName = icon;
            }
        }

        private List<EditorTab> _tabs = new List<EditorTab>();
        private int _currentTabIndex = 0;

        protected virtual void OnEnable()
        {
            s_ShowNavigation = EditorPrefs.GetBool(s_ShowNavigationKey);
            s_Editors.Add(this);
            RegisterStaticOnSceneGUI();

            m_Navigation = serializedObject.FindProperty("m_Navigation");
            m_MainTransition = serializedObject.FindProperty("m_MainTransition");
            m_Interactable = serializedObject.FindProperty("m_Interactable");
            m_animator = serializedObject.FindProperty("_animator");
            // load customSkin once in base; 调整路径为你项目中 GUISkin 的实际路径（必要时修改）
            customSkin = AssetDatabase.LoadAssetAtPath<GUISkin>("Packages/com.alicizax.unity.ui.extension/Editor/Res/GUISkin/UIExtensionGUISkin.guiskin");

            // Ensure default Image tab exists and contains DrawSelectableInspector
            EnsureDefaultImageTab();
        }

        protected virtual void OnDisable()
        {
            s_Editors.Remove(this);
            RegisterStaticOnSceneGUI();
            _tabs.Clear();
            _currentTabIndex = 0;
        }

        #region Tab API (Register / Append / Remove / Unregister)

        /// <summary>
        /// 注册一个新 tab（如果已存在同名 tab，则替换其 icon 和清空回调，再加入 drawCallback）
        /// </summary>
        protected void RegisterTab(string title, string iconName, Action drawCallback)
        {
            if (string.IsNullOrEmpty(title)) return;
            var tab = _tabs.Find(t => t.title == title);
            if (tab == null)
            {
                tab = new EditorTab(title, iconName);
                _tabs.Add(tab);
            }
            else
            {
                tab.iconName = iconName;
                tab.callbacks.Clear();
            }

            if (drawCallback != null)
                tab.callbacks.Add(drawCallback);
        }

        /// <summary>
        /// 在已存在 tab 后追加一个 callback；若 tab 不存在则创建并追加。
        /// </summary>
        protected void AppendToTab(string title, Action drawCallback, bool last = true)
        {
            if (string.IsNullOrEmpty(title) || drawCallback == null) return;
            var tab = _tabs.Find(t => t.title == title);
            if (tab == null)
            {
                tab = new EditorTab(title, "d_DefaultAsset Icon");
                _tabs.Add(tab);
            }

            // 避免重复追加（简单判断）
            if (!tab.callbacks.Contains(drawCallback))
            {
                if (last)
                {
                    tab.callbacks.Add(drawCallback);
                }
                else
                {
                    tab.callbacks.Insert(0, drawCallback);
                }
            }
        }

        /// <summary>
        /// 从指定 tab 中移除某个 callback（子类在 OnDisable 应该调用以清理）
        /// </summary>
        protected void RemoveCallbackFromTab(string title, Action drawCallback)
        {
            if (string.IsNullOrEmpty(title) || drawCallback == null) return;
            var tab = _tabs.Find(t => t.title == title);
            if (tab == null) return;
            tab.callbacks.RemoveAll(cb => cb == drawCallback);
        }

        /// <summary>
        /// 卸载整个 tab（移除该 title 的所有 callbacks 与 tab 本身）。
        /// 如果当前选中的索引超出范围，会自动修正为合法值。
        /// 注意：基类默认创建的 "Image" 页签通常不应被移除，若需要保护可以在此加判断。
        /// </summary>
        protected void UnregisterTab(string title)
        {
            if (string.IsNullOrEmpty(title)) return;
            _tabs.RemoveAll(t => t.title == title);
            // 修正当前索引，避免越界
            if (_tabs.Count == 0)
            {
                _currentTabIndex = 0;
            }
            else if (_currentTabIndex >= _tabs.Count)
            {
                _currentTabIndex = Mathf.Max(0, _tabs.Count - 1);
            }
        }

        private void EnsureDefaultImageTab()
        {
            // 如果还没有 Image tab，创建并把 DrawSelectableInspector 放到第一个 callback
            var tab = _tabs.Find(t => t.title == "Image");
            if (tab == null)
            {
                tab = new EditorTab("Image", "d_Texture Icon");
                _tabs.Insert(0, tab);
            }

            // 确保基类绘制在 Image 页签的第一个 callback （避免重复）
            if (!tab.callbacks.Contains(DrawSelectableInspector))
                tab.callbacks.Insert(0, DrawSelectableInspector);
        }

        #endregion

        protected void DrawToggleShowNavigation()
        {
            Rect toggleRect = EditorGUILayout.GetControlRect();
            toggleRect.xMin += EditorGUIUtility.labelWidth;
            EditorGUI.BeginChangeCheck();
            s_ShowNavigation = GUI.Toggle(toggleRect, s_ShowNavigation, m_VisualizeNavigation, EditorStyles.miniButton);
            if (EditorGUI.EndChangeCheck())
            {
                EditorPrefs.SetBool(s_ShowNavigationKey, s_ShowNavigation);
                SceneView.RepaintAll();
            }
        }

        #region Scene GUI visualization (unchanged)

        private void RegisterStaticOnSceneGUI()
        {
            SceneView.duringSceneGui -= StaticOnSceneGUI;
            if (s_Editors.Count > 0)
                SceneView.duringSceneGui += StaticOnSceneGUI;
        }

        private static void StaticOnSceneGUI(SceneView view)
        {
            if (!s_ShowNavigation)
                return;

            UXSelectable[] selectables = UXSelectable.allSelectablesArray;

            for (int i = 0; i < selectables.Length; i++)
            {
                UXSelectable s = selectables[i];
                if (StageUtility.IsGameObjectRenderedByCamera(s.gameObject, Camera.current))
                    DrawNavigationForSelectable(s);
            }
        }

        private static void DrawNavigationForSelectable(UXSelectable sel)
        {
            if (sel == null)
                return;

            Transform transform = sel.transform;
            bool active = Selection.transforms.Any(e => e == transform);

            Handles.color = new Color(1.0f, 0.6f, 0.2f, active ? 1.0f : 0.4f);
            DrawNavigationArrow(-Vector2.right, sel, sel.FindSelectableOnLeft());
            DrawNavigationArrow(Vector2.up, sel, sel.FindSelectableOnUp());

            Handles.color = new Color(1.0f, 0.9f, 0.1f, active ? 1.0f : 0.4f);
            DrawNavigationArrow(Vector2.right, sel, sel.FindSelectableOnRight());
            DrawNavigationArrow(-Vector2.up, sel, sel.FindSelectableOnDown());
        }

        const float kArrowThickness = 2.5f;
        const float kArrowHeadSize = 1.2f;

        private static void DrawNavigationArrow(Vector2 direction, UXSelectable fromObj, UXSelectable toObj)
        {
            if (fromObj == null || toObj == null)
                return;
            Transform fromTransform = fromObj.transform;
            Transform toTransform = toObj.transform;

            Vector2 sideDir = new Vector2(direction.y, -direction.x);
            Vector3 fromPoint = fromTransform.TransformPoint(GetPointOnRectEdge(fromTransform as RectTransform, direction));
            Vector3 toPoint = toTransform.TransformPoint(GetPointOnRectEdge(toTransform as RectTransform, -direction));
            float fromSize = HandleUtility.GetHandleSize(fromPoint) * 0.05f;
            float toSize = HandleUtility.GetHandleSize(toPoint) * 0.05f;
            fromPoint += fromTransform.TransformDirection(sideDir) * fromSize;
            toPoint += toTransform.TransformDirection(sideDir) * toSize;
            float length = Vector3.Distance(fromPoint, toPoint);
            Vector3 fromTangent = fromTransform.rotation * direction * length * 0.3f;
            Vector3 toTangent = toTransform.rotation * -direction * length * 0.3f;

            Handles.DrawBezier(fromPoint, toPoint, fromPoint + fromTangent, toPoint + toTangent, Handles.color, null, kArrowThickness);
            Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction - sideDir) * toSize * kArrowHeadSize);
            Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction + sideDir) * toSize * kArrowHeadSize);
        }

        private static Vector3 GetPointOnRectEdge(RectTransform rect, Vector2 dir)
        {
            if (rect == null)
                return Vector3.zero;
            if (dir != Vector2.zero)
                dir /= Mathf.Max(Mathf.Abs(dir.x), Mathf.Abs(dir.y));
            dir = rect.rect.center + Vector2.Scale(rect.rect.size, dir * 0.5f);
            return dir;
        }

        #endregion

        /// <summary>
        /// 子类调用：绘制基类的通用 Inspector（Navigation / Interactable / Main Transition）
        /// </summary>
        protected void DrawSelectableInspector()
        {
            if (m_Navigation != null)
            {
                var modeProp = m_Navigation.FindPropertyRelative("m_Mode");

                EditorGUI.BeginChangeCheck();
                EditorGUI.showMixedValue = modeProp.hasMultipleDifferentValues;

                UXNavigation.Mode cur = (UXNavigation.Mode)modeProp.intValue;
                UXNavigation.Mode next = (UXNavigation.Mode)EditorGUILayout.EnumFlagsField("Navigation", cur);

                EditorGUI.showMixedValue = false;
                if (EditorGUI.EndChangeCheck())
                {
                    modeProp.intValue = (int)next;
                }

                int explicitMask = (int)UXNavigation.Mode.Explicit;
                int value = modeProp.intValue;
                bool onlyExplicit = !modeProp.hasMultipleDifferentValues && (value & explicitMask) == explicitMask && (value & ~explicitMask) == 0;

                if (onlyExplicit)
                {
                    EditorGUILayout.PropertyField(m_Navigation.FindPropertyRelative("m_SelectOnUp"));
                    EditorGUILayout.PropertyField(m_Navigation.FindPropertyRelative("m_SelectOnDown"));
                    EditorGUILayout.PropertyField(m_Navigation.FindPropertyRelative("m_SelectOnLeft"));
                    EditorGUILayout.PropertyField(m_Navigation.FindPropertyRelative("m_SelectOnRight"));
                }

                serializedObject.ApplyModifiedProperties();
            }


            DrawToggleShowNavigation();

            if (customSkin != null)
            {
                var interactable = GUILayoutHelper.DrawToggle(m_Interactable.boolValue, customSkin, "Interactable");
                if (interactable != m_Interactable.boolValue)
                {
                    (target as UXSelectable).Interactable = interactable;
                    m_Interactable.boolValue = interactable;
                    var selProp = serializedObject.FindProperty("m_SelectionState");
                    if (selProp != null)
                    {
                        selProp.enumValueIndex = interactable ? (int)UXSelectable.SelectionState.Normal : (int)UXSelectable.SelectionState.Disabled;
                    }
                }
            }

            GUILayout.Space(1);
            serializedObject.ApplyModifiedProperties();
            DrawSelfTransition();
            GUILayout.Space(5);
        }

        #region MainTransition helpers (moved to base)

        protected virtual void DrawSelfTransition()
        {
            if (m_MainTransition == null)
                return;

            GUILayout.BeginVertical(EditorStyles.helpBox);
            EditorGUILayout.LabelField("Main Transition", EditorStyles.boldLabel);
            SerializedProperty targetGraphic = m_MainTransition.FindPropertyRelative("targetGraphic");
            var graphic = targetGraphic.objectReferenceValue as Graphic;
            if (graphic == null)
            {
                graphic = (target as UXSelectable).GetComponent<Graphic>();
                if (targetGraphic != null)
                    targetGraphic.objectReferenceValue = graphic;
            }

            SerializedProperty transition = m_MainTransition.FindPropertyRelative("transition");
            var currentTransition = GetTransition(transition);

            switch (currentTransition)
            {
                case Selectable.Transition.ColorTint:
                case Selectable.Transition.SpriteSwap:
                    EditorGUILayout.PropertyField(targetGraphic);
                    break;
            }

            EditorGUILayout.PropertyField(transition);

            Animator animator = null;
            if (target is UXSelectable anima)
            {
                animator = anima.GetComponent<Animator>();
            }
            else if (m_animator.objectReferenceValue != null)
            {
                animator = (Animator)m_animator.objectReferenceValue;
            }

            switch (currentTransition)
            {
                case Selectable.Transition.ColorTint:
                    if (graphic == null)
                        EditorGUILayout.HelpBox("需要Graphic组件来使用颜色过渡", MessageType.Warning);
                    break;
                case Selectable.Transition.SpriteSwap:
                    if (!(graphic is Image))
                        EditorGUILayout.HelpBox("需要Image组件来使用精灵切换", MessageType.Warning);
                    break;
                case Selectable.Transition.Animation:
                    if (animator == null)
                        EditorGUILayout.HelpBox("需要Animator组件来使用动画切换", MessageType.Warning);
                    break;
            }

            switch (currentTransition)
            {
                case Selectable.Transition.ColorTint:
                    CheckAndSetColorDefaults(m_MainTransition.FindPropertyRelative("colors"), targetGraphic);
                    EditorGUILayout.PropertyField(m_MainTransition.FindPropertyRelative("colors"));
                    break;
                case Selectable.Transition.SpriteSwap:
                    EditorGUILayout.PropertyField(m_MainTransition.FindPropertyRelative("spriteState"));
                    break;
                case Selectable.Transition.Animation:
                    EditorGUILayout.PropertyField(m_MainTransition.FindPropertyRelative("animationTriggers"));
                    if (animator == null || animator.runtimeAnimatorController == null)
                    {
                        if (GUILayout.Button("Auto Generate Animation"))
                        {
                            var animationTrigger = m_MainTransition.FindPropertyRelative("animationTriggers");
                            var controller = GenerateSelectableAnimatorContoller(animationTrigger, target);
                            if (controller != null)
                            {
                                if (animator == null)
                                    animator = (target as UXSelectable).gameObject.AddComponent<Animator>();

                                UnityEditor.Animations.AnimatorController.SetAnimatorController(animator, controller);
                            }
                        }
                    }

                    break;
            }

            if (graphic != null && currentTransition != (Selectable.Transition)transition.enumValueIndex &&
                ((Selectable.Transition)transition.enumValueIndex == Selectable.Transition.Animation ||
                 (Selectable.Transition)transition.enumValueIndex == Selectable.Transition.None))
            {
                graphic.canvasRenderer.SetColor(Color.white);
            }

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

        protected void CheckAndSetColorDefaults(SerializedProperty colorBlock, SerializedProperty targetGraphic)
        {
            if (colorBlock == null) return;

            bool isDirty = false;
            string[] colorProps = new string[] { "m_NormalColor", "m_HighlightedColor", "m_PressedColor", "m_SelectedColor", "m_DisabledColor" };
            foreach (var propName in colorProps)
            {
                SerializedProperty prop = colorBlock.FindPropertyRelative(propName);
                if (prop == null) continue;
                Color color = prop.colorValue;
                if (color.r == 0 && color.g == 0 && color.b == 0 && color.a == 0)
                {
                    isDirty = true;
                    if (prop.name == "m_PressedColor")
                    {
                        prop.colorValue = new Color(0.7843137f, 0.7843137f, 0.7843137f, 1.0f);
                    }
                    else if (prop.name == "m_DisabledColor")
                    {
                        prop.colorValue = new Color(0.7843137f, 0.7843137f, 0.7843137f, 0.5f);
                    }
                    else
                    {
                        prop.colorValue = Color.white;
                    }
                }
            }

            SerializedProperty fadeDuration = colorBlock.FindPropertyRelative("m_FadeDuration");
            SerializedProperty m_ColorMultiplier = colorBlock.FindPropertyRelative("m_ColorMultiplier");
            if (isDirty)
            {
                if (m_ColorMultiplier != null) m_ColorMultiplier.floatValue = 1f;
                if (fadeDuration != null) fadeDuration.floatValue = 0.1f;
            }

            var graphic = targetGraphic != null ? targetGraphic.objectReferenceValue as Graphic : null;
            if (graphic != null)
            {
                if (!EditorApplication.isPlaying)
                {
                    SerializedProperty normalColorProp = colorBlock.FindPropertyRelative("m_NormalColor");
                    if (normalColorProp != null)
                    {
                        Color color = normalColorProp.colorValue;
                        graphic.canvasRenderer.SetColor(color);
                    }
                }
            }
        }

        protected static UnityEditor.Animations.AnimatorController GenerateSelectableAnimatorContoller(SerializedProperty property, UnityEngine.Object targetObj)
        {
            if (targetObj == null || property == null)
                return null;

            var targetAsGO = (targetObj as UXSelectable);
            var path = GetSaveControllerPath(targetAsGO);
            if (string.IsNullOrEmpty(path))
                return null;
            SerializedProperty normalTrigger = property.FindPropertyRelative("m_NormalTrigger");
            SerializedProperty highlightedTrigger = property.FindPropertyRelative("m_HighlightedTrigger");
            SerializedProperty pressedTrigger = property.FindPropertyRelative("m_PressedTrigger");
            SerializedProperty selectedTrigger = property.FindPropertyRelative("m_SelectedTrigger");
            SerializedProperty disabledTrigger = property.FindPropertyRelative("m_DisabledTrigger");

            var normalName = string.IsNullOrEmpty(normalTrigger.stringValue) ? "Normal" : normalTrigger.stringValue;
            var highlightedName = string.IsNullOrEmpty(highlightedTrigger.stringValue) ? "Highlighted" : highlightedTrigger.stringValue;
            var pressedName = string.IsNullOrEmpty(pressedTrigger.stringValue) ? "Pressed" : pressedTrigger.stringValue;
            var selectedName = string.IsNullOrEmpty(selectedTrigger.stringValue) ? "Selected" : selectedTrigger.stringValue;
            var disabledName = string.IsNullOrEmpty(disabledTrigger.stringValue) ? "Disabled" : disabledTrigger.stringValue;

            var controller = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath(path);
            GenerateTriggerableTransition(normalName, controller);
            GenerateTriggerableTransition(highlightedName, controller);
            GenerateTriggerableTransition(pressedName, controller);
            GenerateTriggerableTransition(selectedName, controller);
            GenerateTriggerableTransition(disabledName, controller);

            AssetDatabase.ImportAsset(path);

            return controller;
        }

        private static AnimationClip GenerateTriggerableTransition(string name, UnityEditor.Animations.AnimatorController controller)
        {
            // Create the clip
            var clip = UnityEditor.Animations.AnimatorController.AllocateAnimatorClip(name);
            AssetDatabase.AddObjectToAsset(clip, controller);

            // Create a state in the animatior controller for this clip
            var state = controller.AddMotion(clip);

            // Add a transition property
            controller.AddParameter(name, AnimatorControllerParameterType.Trigger);

            // Add an any state transition
            var stateMachine = controller.layers[0].stateMachine;
            var transition = stateMachine.AddAnyStateTransition(state);
            transition.AddCondition(UnityEditor.Animations.AnimatorConditionMode.If, 0, name);
            return clip;
        }

        private static string GetSaveControllerPath(UXSelectable target)
        {
            var defaultName = target != null ? target.gameObject.name : "NewController";
            var message = string.Format("Create a new animator for the game object '{0}':", defaultName);
            return EditorUtility.SaveFilePanelInProject("New Animation Contoller", defaultName, "controller", message);
        }

        protected static Selectable.Transition GetTransition(SerializedProperty transition)
        {
            if (transition == null) return Selectable.Transition.None;
            return (Selectable.Transition)transition.enumValueIndex;
        }

        #endregion

        #region Tab drawing entry

        public override void OnInspectorGUI()
        {
            serializedObject.Update();
            DrawTabs();
            serializedObject.ApplyModifiedProperties();
        }

        protected void DrawTabs()
        {
            if (_tabs == null || _tabs.Count == 0)
            {
                // fallback: draw base directly
                DrawSelectableInspector();
                return;
            }

            EditorGUILayout.BeginHorizontal();

            for (int i = 0; i < _tabs.Count; i++)
            {
                var tab = _tabs[i];
                bool isActive = (i == _currentTabIndex);
                var style = new GUIStyle(EditorStyles.toolbarButton) { fixedHeight = 25, fontSize = 11, fontStyle = isActive ? FontStyle.Bold : FontStyle.Normal };

                var content = new GUIContent(EditorGUIUtility.IconContent(tab.iconName).image, tab.title);
                if (GUILayout.Button(content, style))
                {
                    _currentTabIndex = i;
                }

                if (isActive)
                {
                    Rect rect = GUILayoutUtility.GetLastRect();
                    EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - 2, rect.width, 2), new Color(0.1f, 0.5f, 0.9f));
                }
            }

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

            // draw current tab content (call all callbacks registered for the tab)
            var callbacks = _tabs[_currentTabIndex].callbacks;
            if (callbacks != null)
            {
                foreach (var cb in callbacks)
                {
                    try
                    {
                        cb?.Invoke();
                    }
                    catch (UnityEngine.ExitGUIException)
                    {
                        // Unity 用 ExitGUIException 来中断 GUI 流程 —— 需要重新抛出，让 Unity 处理
                        throw;
                    }
                    catch (Exception e)
                    {
                        // 仅记录真正的异常
                        Debug.LogException(e);
                    }
                }
            }
        }

        #endregion
    }
}
