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

[CustomEditor(typeof(WorldManager))]
public class WorldManagerEditor : Editor
{
    private bool _showTilemap = true;
    private bool _showNPC = true;
    private bool _showCamera = true;
    private int _selectedNPC = -1;
    private bool _showNPCList = true;
    private bool _allRoundRobin;
    private NPCData.Language _editorLang = NPCData.Language.EN;
    private HashSet<int> _roundRobinSet = new();
    private GUIContent _camIcon;

    public override void OnInspectorGUI()
    {
        if (_camIcon == null)
            _camIcon = EditorGUIUtility.IconContent("SceneViewCamera");

        serializedObject.Update();

        DrawHeader("Village World Manager");

        // Language setting
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("Language", EditorStyles.boldLabel, GUILayout.Width(70));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_currentLanguage"), GUIContent.none);
        EditorGUILayout.EndHorizontal();
        _editorLang = (NPCData.Language)serializedObject.FindProperty("_currentLanguage").enumValueIndex;

        EditorGUILayout.Space(4);
        _showTilemap = DrawSection("Tilemap", _showTilemap, DrawTilemapSection);
        _showNPC = DrawSection("NPC Data", _showNPC, DrawNPCSection);
        _showCamera = DrawSection("Camera", _showCamera, DrawCameraSection);

        serializedObject.ApplyModifiedProperties();
    }

    void DrawHeader(string title)
    {
        EditorGUILayout.Space(4);
        var style = new GUIStyle(EditorStyles.boldLabel)
        {
            fontSize = 14,
            alignment = TextAnchor.MiddleCenter
        };
        EditorGUILayout.LabelField(title, style);
        DrawLine();
    }

    bool DrawSection(string title, bool isOpen, System.Action drawContent)
    {
        EditorGUILayout.Space(2);
        isOpen = EditorGUILayout.Foldout(isOpen, title, true, EditorStyles.foldoutHeader);
        if (isOpen)
        {
            EditorGUI.indentLevel++;
            drawContent();
            EditorGUI.indentLevel--;
        }
        return isOpen;
    }

    // --- Tilemap ---
    void DrawTilemapSection()
    {
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_pathfinder"));

        var pathfinder = GetChildSO("_pathfinder");
        if (pathfinder == null) { DrawMissing("Pathfinder"); return; }

        pathfinder.Update();

        var sets = pathfinder.FindProperty("_tilemapSets");
        var activeIdx = pathfinder.FindProperty("_activeSetIndex");

        // Active map selector
        if (sets.arraySize > 0)
        {
            var names = new string[sets.arraySize];
            for (int i = 0; i < sets.arraySize; i++)
            {
                var n = sets.GetArrayElementAtIndex(i).FindPropertyRelative("Name").stringValue;
                names[i] = string.IsNullOrEmpty(n) ? $"Set {i}" : n;
            }

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("Active Map", GUILayout.Width(80));
            int newIdx = EditorGUILayout.Popup(activeIdx.intValue, names);
            if (newIdx != activeIdx.intValue)
            {
                activeIdx.intValue = newIdx;
                pathfinder.ApplyModifiedProperties();

                var wm = (WorldManager)target;
                if (Application.isPlaying)
                    wm.SetActiveMap(newIdx);
                else
                    ((Pathfinder)pathfinder.targetObject).SetActiveMap(newIdx);
            }
            EditorGUILayout.EndHorizontal();
        }

        EditorGUILayout.Space(4);

        // Map set list
        for (int i = 0; i < sets.arraySize; i++)
        {
            var set = sets.GetArrayElementAtIndex(i);
            var setName = set.FindPropertyRelative("Name");
            bool isActive = i == activeIdx.intValue;

            EditorGUILayout.BeginVertical(isActive ? "box" : EditorStyles.helpBox);

            EditorGUILayout.BeginHorizontal();
            if (isActive)
                EditorGUILayout.LabelField("*", EditorStyles.boldLabel, GUILayout.Width(14));
            EditorGUILayout.PropertyField(setName, GUIContent.none);
            if (GUILayout.Button("-", GUILayout.Width(20)))
            {
                sets.DeleteArrayElementAtIndex(i);
                break;
            }
            EditorGUILayout.EndHorizontal();

            EditorGUILayout.PropertyField(set.FindPropertyRelative("Root"));
            EditorGUILayout.PropertyField(set.FindPropertyRelative("BaseTilemap"));
            EditorGUILayout.PropertyField(set.FindPropertyRelative("BlockTilemaps"), true);

            EditorGUILayout.EndVertical();
        }

        if (GUILayout.Button("+ Add Map Set"))
        {
            sets.InsertArrayElementAtIndex(sets.arraySize);
            var newSet = sets.GetArrayElementAtIndex(sets.arraySize - 1);
            newSet.FindPropertyRelative("Name").stringValue = $"Map {sets.arraySize}";
        }

        pathfinder.ApplyModifiedProperties();
    }

    // --- NPC ---
    void DrawNPCSection()
    {
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_npcManager"));

        var npcMgr = GetChildSO("_npcManager");
        if (npcMgr == null) { DrawMissing("NPCManager"); return; }

        npcMgr.Update();

        var npcList = npcMgr.FindProperty("_npcDataList");
        var bubbleMgr = npcMgr.FindProperty("_speechBubbleManager");
        var spawnParent = npcMgr.FindProperty("_spawnParent");
        var pathfinder = npcMgr.FindProperty("_pathfinder");

        EditorGUILayout.PropertyField(spawnParent);
        EditorGUILayout.PropertyField(bubbleMgr);
        EditorGUILayout.PropertyField(pathfinder);
        EditorGUILayout.Space(4);

        EditorGUILayout.BeginHorizontal();

        // All toggle
        var prevColor = GUI.backgroundColor;
        if (_allRoundRobin) GUI.backgroundColor = Color.cyan;
        if (GUILayout.Button(_camIcon, GUILayout.Width(28), GUILayout.Height(18)))
        {
            _allRoundRobin = !_allRoundRobin;
            _roundRobinSet.Clear();
            if (_allRoundRobin)
                for (int j = 0; j < npcList.arraySize; j++) _roundRobinSet.Add(j);
            ApplyRoundRobin(npcList);
        }
        GUI.backgroundColor = prevColor;

        // List fold
        if (GUILayout.Button(_showNPCList ? "v" : ">", GUILayout.Width(24), GUILayout.Height(18)))
            _showNPCList = !_showNPCList;

        EditorGUILayout.LabelField($"NPC List ({npcList.arraySize})", EditorStyles.boldLabel);
        EditorGUILayout.EndHorizontal();

        if (!_showNPCList) return;

        for (int i = 0; i < npcList.arraySize; i++)
        {
            var element = npcList.GetArrayElementAtIndex(i);
            var data = element.objectReferenceValue as NPCData;
            string label = data != null ? data.NPCName : "(empty)";

            bool isSelected = _selectedNPC == i;
            bool isInRoundRobin = _roundRobinSet.Contains(i);
            EditorGUILayout.BeginHorizontal();

            // Round robin toggle
            var itemColor = GUI.backgroundColor;
            if (isInRoundRobin) GUI.backgroundColor = Color.cyan;
            if (GUILayout.Button(_camIcon, GUILayout.Width(28), GUILayout.Height(18)))
            {
                if (isInRoundRobin)
                    _roundRobinSet.Remove(i);
                else
                    _roundRobinSet.Add(i);
                _allRoundRobin = _roundRobinSet.Count == npcList.arraySize;
                ApplyRoundRobin(npcList);
            }
            GUI.backgroundColor = itemColor;

            if (GUILayout.Button(isSelected ? "v" : ">", GUILayout.Width(24), GUILayout.Height(18)))
                _selectedNPC = isSelected ? -1 : i;

            EditorGUILayout.PropertyField(element, new GUIContent(label));

            if (GUILayout.Button("-", GUILayout.Width(24), GUILayout.Height(18)))
            {
                npcList.DeleteArrayElementAtIndex(i);
                _roundRobinSet.Remove(i);
                if (_selectedNPC >= npcList.arraySize) _selectedNPC = -1;
                ApplyRoundRobin(npcList);
                break;
            }

            EditorGUILayout.EndHorizontal();

            if (_selectedNPC == i && data != null)
                DrawNPCDetail(data);
        }

        EditorGUILayout.Space(4);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("+ Add Slot"))
            npcList.InsertArrayElementAtIndex(npcList.arraySize);
        if (GUILayout.Button("Create New NPCData"))
            CreateNewNPCData(npcList);
        EditorGUILayout.EndHorizontal();

        npcMgr.ApplyModifiedProperties();
    }

    void DrawNPCDetail(NPCData data)
    {
        EditorGUI.indentLevel++;
        var so = new SerializedObject(data);
        so.Update();

        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
        EditorGUILayout.PropertyField(so.FindProperty("NPCName"));
        EditorGUILayout.PropertyField(so.FindProperty("Prefab"));
        EditorGUILayout.PropertyField(so.FindProperty("MoveSpeed"));

        EditorGUILayout.Space(4);

        EditorGUILayout.LabelField("Dialogues (" + _editorLang + ")", EditorStyles.boldLabel);
        EditorGUILayout.Space(2);

        if (_editorLang == NPCData.Language.EN)
        {
            DrawStateDialoguesList(so.FindProperty("StateDialogues"));
        }
        else
        {
            DrawLocalizedDialoguesList(so, data);
        }

        EditorGUILayout.EndVertical();

        if (so.ApplyModifiedProperties())
            EditorUtility.SetDirty(data);

        EditorGUI.indentLevel--;
    }

    void DrawStateDialoguesList(SerializedProperty dialogues)
    {
        if (dialogues.arraySize == 0)
        {
            if (GUILayout.Button("Initialize (Idle/Move/Talk/Despair)"))
            {
                dialogues.arraySize = 4;
                for (int i = 0; i < 4; i++)
                    dialogues.GetArrayElementAtIndex(i)
                        .FindPropertyRelative("State").enumValueIndex = i;
            }
            return;
        }

        for (int s = 0; s < dialogues.arraySize; s++)
        {
            var entry = dialogues.GetArrayElementAtIndex(s);
            var stateProp = entry.FindPropertyRelative("State");
            var linesProp = entry.FindPropertyRelative("Dialogues");
            string stateName = stateProp.enumDisplayNames[stateProp.enumValueIndex];

            EditorGUILayout.BeginVertical("box");
            EditorGUILayout.LabelField(stateName, EditorStyles.boldLabel);

            for (int l = 0; l < linesProp.arraySize; l++)
            {
                EditorGUILayout.BeginHorizontal();
                linesProp.GetArrayElementAtIndex(l).stringValue =
                    EditorGUILayout.TextField(linesProp.GetArrayElementAtIndex(l).stringValue);
                if (GUILayout.Button("-", GUILayout.Width(20)))
                {
                    linesProp.DeleteArrayElementAtIndex(l);
                    break;
                }
                EditorGUILayout.EndHorizontal();
            }

            if (GUILayout.Button("+ Add Line", EditorStyles.miniButton))
                linesProp.InsertArrayElementAtIndex(linesProp.arraySize);

            EditorGUILayout.EndVertical();
        }
    }

    void DrawLocalizedDialoguesList(SerializedObject so, NPCData data)
    {
        var locProp = so.FindProperty("LocalizedDialogues");

        int langIdx = -1;
        for (int i = 0; i < locProp.arraySize; i++)
        {
            if (locProp.GetArrayElementAtIndex(i).FindPropertyRelative("Lang").enumValueIndex == (int)_editorLang)
            {
                langIdx = i;
                break;
            }
        }

        if (langIdx == -1)
        {
            if (GUILayout.Button($"Create {_editorLang} dialogues"))
            {
                langIdx = locProp.arraySize;
                locProp.InsertArrayElementAtIndex(langIdx);
                var newEntry = locProp.GetArrayElementAtIndex(langIdx);
                newEntry.FindPropertyRelative("Lang").enumValueIndex = (int)_editorLang;

                var enDialogues = so.FindProperty("StateDialogues");
                var newDialogues = newEntry.FindPropertyRelative("StateDialogues");
                newDialogues.arraySize = enDialogues.arraySize;
                for (int i = 0; i < enDialogues.arraySize; i++)
                {
                    var src = enDialogues.GetArrayElementAtIndex(i);
                    var dst = newDialogues.GetArrayElementAtIndex(i);
                    dst.FindPropertyRelative("State").enumValueIndex =
                        src.FindPropertyRelative("State").enumValueIndex;
                    dst.FindPropertyRelative("AnimationIndex").intValue =
                        src.FindPropertyRelative("AnimationIndex").intValue;
                    dst.FindPropertyRelative("Dialogues").arraySize = 0;
                }
                so.ApplyModifiedProperties();
            }
            return;
        }

        var entry = locProp.GetArrayElementAtIndex(langIdx);
        DrawStateDialoguesList(entry.FindPropertyRelative("StateDialogues"));

        EditorGUILayout.Space(4);
        if (GUILayout.Button($"Delete {_editorLang}", EditorStyles.miniButton))
        {
            locProp.DeleteArrayElementAtIndex(langIdx);
            so.ApplyModifiedProperties();
        }
    }

    // --- Camera ---
    void DrawCameraSection()
    {
        EditorGUILayout.PropertyField(serializedObject.FindProperty("_cameraController"));

        var camSO = GetChildSO("_cameraController");
        if (camSO == null) { DrawMissing("CameraController"); return; }

        camSO.Update();

        EditorGUILayout.Space(2);
        EditorGUILayout.BeginVertical(EditorStyles.helpBox);
        EditorGUILayout.PropertyField(camSO.FindProperty("_mode"));
        EditorGUILayout.PropertyField(camSO.FindProperty("_zoomSpeed"));
        EditorGUILayout.PropertyField(camSO.FindProperty("_minZoom"));
        EditorGUILayout.PropertyField(camSO.FindProperty("_maxZoom"));
        EditorGUILayout.Space(2);
        EditorGUILayout.LabelField("Follow NPC", EditorStyles.boldLabel);
        EditorGUILayout.PropertyField(camSO.FindProperty("_followTarget"));
        EditorGUILayout.PropertyField(camSO.FindProperty("_followSpeed"));
        EditorGUILayout.PropertyField(camSO.FindProperty("_followOffsetY"));
        EditorGUILayout.Space(2);
        EditorGUILayout.LabelField("Round Robin", EditorStyles.boldLabel);
        EditorGUILayout.PropertyField(camSO.FindProperty("_switchInterval"));
        if (_roundRobinSet.Count > 0)
            EditorGUILayout.HelpBox($"{_roundRobinSet.Count} NPC(s) in rotation", MessageType.Info);
        EditorGUILayout.EndVertical();

        camSO.ApplyModifiedProperties();
    }

    // --- Utilities ---
    SerializedObject GetChildSO(string propName)
    {
        var prop = serializedObject.FindProperty(propName);
        if (prop == null || prop.objectReferenceValue == null) return null;
        return new SerializedObject(prop.objectReferenceValue);
    }

    void DrawMissing(string name)
    {
        EditorGUILayout.HelpBox($"{name} is not assigned.", MessageType.Warning);
    }

    void CreateNewNPCData(SerializedProperty npcList)
    {
        string path = EditorUtility.SaveFilePanelInProject(
            "Create NPCData", "NewNPC", "asset",
            "Save new NPC data asset",
            "Assets/SampleVillage/Data");

        if (string.IsNullOrEmpty(path)) return;

        var data = ScriptableObject.CreateInstance<NPCData>();
        data.NPCName = System.IO.Path.GetFileNameWithoutExtension(path);
        AssetDatabase.CreateAsset(data, path);
        AssetDatabase.SaveAssets();

        int idx = npcList.arraySize;
        npcList.InsertArrayElementAtIndex(idx);
        npcList.GetArrayElementAtIndex(idx).objectReferenceValue = data;
    }

    void ApplyRoundRobin(SerializedProperty npcList)
    {
        var camProp = serializedObject.FindProperty("_cameraController");
        var cam = camProp.objectReferenceValue as CameraController;
        if (cam == null || !Application.isPlaying) return;

        if (_roundRobinSet.Count == 0)
        {
            cam.CameraMode = CameraController.Mode.FreeDrag;
            cam.FollowTarget = null;
            return;
        }

        var targets = new List<Transform>();
        foreach (var idx in _roundRobinSet)
        {
            if (idx >= npcList.arraySize) continue;
            var data = npcList.GetArrayElementAtIndex(idx).objectReferenceValue as NPCData;
            if (data == null) continue;
            var go = GameObject.Find(data.NPCName);
            if (go != null) targets.Add(go.transform);
        }

        if (targets.Count > 0)
        {
            cam.SetRoundRobinTargets(targets.ToArray());
            cam.CameraMode = CameraController.Mode.RoundRobin;
        }
    }

    void DrawLine()
    {
        EditorGUILayout.Space(2);
        var rect = EditorGUILayout.GetControlRect(false, 1);
        EditorGUI.DrawRect(rect, new Color(0.5f, 0.5f, 0.5f, 0.5f));
        EditorGUILayout.Space(2);
    }
}
