using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.IO.Compression;
using System.Xml.Linq;

public static class ConfigLinkViewerCallbacks
{
    public static Action ExportConfigCallback;
    public static Func<string> FallbackExcelPathCallback;
}

[InitializeOnLoad]
public static class ConfigLinkViewerAutoSetup
{
    static ConfigLinkViewerAutoSetup()
    {
        TryRegisterExportConfig();
        TryRegisterFallbackExcelPath();
    }

    private static void TryRegisterExportConfig()
    {
        if (ConfigLinkViewerCallbacks.ExportConfigCallback != null) return;
        try
        {
            var type = FindType("MFrame.ConfigDeal");
            if (type == null) return;
            var method = type.GetMethod("ExportConfig",
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (method == null) return;
            ConfigLinkViewerCallbacks.ExportConfigCallback =
                (Action)Delegate.CreateDelegate(typeof(Action), method);
        }
        catch { }
    }

    private static void TryRegisterFallbackExcelPath()
    {
        if (ConfigLinkViewerCallbacks.FallbackExcelPathCallback != null) return;
        try
        {
            var pathConfType = FindType("MFrame.PathConf");
            var editorCfType = FindType("MFrame.ConfigEditorCf");
            if (pathConfType == null || editorCfType == null) return;

            var rootField = pathConfType.GetField("project_root",
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (rootField == null) return;

            var insProp = editorCfType.GetProperty("ins",
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (insProp == null) return;

            var ins = insProp.GetValue(null);
            if (ins == null) return;

            var excelField = ins.GetType().GetField("excel_path",
                System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            if (excelField == null) return;

            ConfigLinkViewerCallbacks.FallbackExcelPathCallback = () =>
            {
                var root = rootField.GetValue(null) as string;
                var inst = insProp.GetValue(null);
                var ep = inst != null ? excelField.GetValue(inst) as string : null;
                return root + ep;
            };
        }
        catch { }
    }

    private static Type FindType(string fullName)
    {
        foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
        {
            var t = asm.GetType(fullName);
            if (t != null) return t;
        }
        return null;
    }
}

public class ConfigLinkViewerWindow : EditorWindow
{
    private enum Tab { View, Config }

    private Tab currentTab = Tab.View;
    private string selectedTableName;
    private Dictionary<string, bool> expandedRelations = new Dictionary<string, bool>();
    private Vector2 relationScrollPos;
    private Vector2 detailScrollPos;
    private Vector2 configScrollPos;
    private string searchFilter = "";
    private bool showOnlyExisting = true;
    private bool showReverseRelations = false;

    [MenuItem("Tools/配置表联动查看器")]
    public static void ShowWindow()
    {
        var window = GetWindow<ConfigLinkViewerWindow>("配置表联动");
        window.minSize = new Vector2(900, 600);
        window.position = new Rect(100, 100, 900, 600);
    }

    private void OnGUI()
    {
        DrawTabBar();
        EditorGUILayout.Space();

        float windowWidth = position.width;
        float windowHeight = position.height;

        if (currentTab == Tab.View)
        {
            DrawViewTab(windowWidth, windowHeight);
        }
        else
        {
            DrawConfigTab(windowWidth, windowHeight);
            DrawLuoTianyiOverlay(windowWidth, windowHeight);
        }
    }

    private void DrawTabBar()
    {
        EditorGUILayout.BeginHorizontal("Toolbar");
        GUILayout.FlexibleSpace();

        GUIStyle tabLeft, tabRight;
        if (currentTab == Tab.View)
        {
            tabLeft = new GUIStyle("ToolbarButton") { fontStyle = FontStyle.Bold };
            tabRight = new GUIStyle("ToolbarButton");
        }
        else
        {
            tabLeft = new GUIStyle("ToolbarButton");
            tabRight = new GUIStyle("ToolbarButton") { fontStyle = FontStyle.Bold };
        }

        if (GUILayout.Button("查看", tabLeft, GUILayout.Width(60)))
        {
            currentTab = Tab.View;
        }
        if (GUILayout.Button("配置", tabRight, GUILayout.Width(60)))
        {
            currentTab = Tab.Config;
        }

        GUILayout.FlexibleSpace();
        EditorGUILayout.EndHorizontal();
    }

    private void DrawViewTab(float windowWidth, float windowHeight)
    {
        DrawViewHeader();
        EditorGUILayout.Space();
        DrawTableSelector(windowWidth, windowHeight);
    }

    private void DrawViewHeader()
    {
        EditorGUILayout.BeginVertical("box");
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("配置表联动关系查看器", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 }, GUILayout.ExpandWidth(true));

        if (GUILayout.Button("清理Excel空行", GUILayout.Height(22), GUILayout.Width(100)))
        {
            EditorApplication.delayCall += CleanExcelEmptyRows;
        }
        if (GUILayout.Button("导出配置", GUILayout.Height(22), GUILayout.Width(80)))
        {
            EditorApplication.delayCall += () =>
            {
                if (ConfigLinkViewerCallbacks.ExportConfigCallback != null)
                {
                    ConfigLinkViewerCallbacks.ExportConfigCallback();
                }
                else
                {
                    EditorUtility.DisplayDialog("提示",
                        "未注册导出配置回调。\n请在项目初始化代码中设置 ConfigLinkViewerCallbacks.ExportConfigCallback。",
                        "确定");
                }
            };
        }
        if (GUILayout.Button("刷新", GUILayout.Height(22), GUILayout.Width(60)))
        {
            ConfigLinkDatabase.ClearCache();
        }
        EditorGUILayout.EndHorizontal();

        string configStatus = ConfigLinkDatabase.HasConfigFile()
            ? "<color=green>已加载 (ConfigLinkData.json)</color>"
            : "<color=yellow>未找到配置文件，请切换到[配置]标签页生成</color>";
        var statusStyle = new GUIStyle(EditorStyles.miniLabel) { richText = true };
        EditorGUILayout.LabelField($"配置状态: {configStatus}", statusStyle);

        EditorGUILayout.EndVertical();
    }

    private void DrawConfigTab(float windowWidth, float windowHeight)
    {
        configScrollPos = EditorGUILayout.BeginScrollView(configScrollPos);
        EditorGUILayout.BeginVertical("box");

        EditorGUILayout.LabelField("项目配置", new GUIStyle(EditorStyles.boldLabel) { fontSize = 14 });
        EditorGUILayout.Space();

        DrawProjectSettings();
        EditorGUILayout.Space();

        DrawExcelPathSettings(windowWidth);
        EditorGUILayout.Space();

        EditorGUILayout.BeginVertical("box");
        EditorGUILayout.LabelField("配置数据生成", EditorStyles.boldLabel);
        EditorGUILayout.LabelField("首次使用或跨项目时，需要先生成配置数据。", EditorStyles.miniLabel);
        EditorGUILayout.Space();

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("生成配置", GUILayout.Height(30)))
        {
            EditorApplication.delayCall += () =>
            {
                ConfigLinkDatabase.GenerateConfigFromExcelFolder();
            };
        }
        if (GUILayout.Button("补全配置", GUILayout.Height(30)))
        {
            EditorApplication.delayCall += RunAIConfigScript;
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("生成配置：从 Excel 文件夹扫描生成 ConfigLinkData.json 骨架", EditorStyles.miniLabel);
        EditorGUILayout.LabelField("补全配置：运行 Python 脚本自动分析引用关系，补全字段关联", EditorStyles.miniLabel);

        EditorGUILayout.EndVertical();

        EditorGUILayout.EndVertical();
        EditorGUILayout.EndScrollView();
    }

    private GUIStyle _artStyle;

    private GUIStyle GetArtStyle(int fontSize)
    {
        if (_artStyle == null || _artStyle.fontSize != fontSize)
        {
            _artStyle = new GUIStyle(EditorStyles.label)
            {
                alignment = TextAnchor.MiddleCenter,
                fontSize = fontSize,
                richText = true,
                wordWrap = false,
                clipping = TextClipping.Overflow,
                normal = { textColor = Color.white },
            };
            var font = Font.CreateDynamicFontFromOSFont("Consolas", fontSize);
            if (font != null)
                _artStyle.font = font;
        }
        return _artStyle;
    }

    private void DrawLuoTianyiOverlay(float windowWidth, float windowHeight)
    {
        float scale = Mathf.Clamp(Mathf.Min(windowWidth, windowHeight) / 600f, 0.35f, 0.7f);
        int fontSize = Mathf.RoundToInt(9 * scale);
        float lineHeight = fontSize * 1.1f;

        string[] lines = {
            "                            _________________",
            "                       ____/:::::::::::::::::\\_____",
            "                    __/::::::::::::::::::::::::::::\\___",
            "                  _/:::::::::::::::::::::::::::::::::::\\__",
            "                _/::::::::::::::::::::::::::::::::::::::::\\_",
            "               /::::::::::::::::::::::::::::::::::::::::::::\\",
            "              |::::::::::::::::::::::::::::::::::::::::::::::\\",
            "             /::::::::::::::::::::::::::::::::::::::::::::::::\\",
            "            |:::/.:::::::;:::::::::::::::::::::::::::::::::::::|",
            "           /:::/.:::::::/..:::::::::::::::::::::::::::::::::::::\\",
            "          |:::|.::::::;/.::::::::::::::::::::::::::::::::::::::::|",
            "          |::/.::::::/..:::::::;;'.::::::::::::::::::::::::::::::|",
            "          |:|.::::/./.::::::;;/..:::::::::::::::::::::::::::::::::|",
            "          `:|.:::|.|.:::::;/..;;;;;;-'.:;;;-':::::::::::::::::::::|",
            "           \\|.:::|.|.:::;/.;;/  -..::'''...:::::::::::::::::::::::|",
            "            \\;;::|.|.::/.;/--__      |::::::::::::::::::::::::::::|",
            "               \\;;\\\\::/|/ =-__ --_  /::::::::::::::::::::::::::::::",
            "                   \\/    /|  -._  |.::::::::::::::::::::::::::::::",
            "                  _.'    ///   /-  ||::::::::::::::::::::::::::::::",
            "              _.-'      //'        ||::::::::::::::::::::::::::::::",
            "             |          -          `|::::::::::::::::::::::::::::::",
            "              \\                      \\:::::::::::::::::::::::::::::",
            "               |                      \\:::::::::::::::::::::",
            "              /                      __/:::::::::::::::::::::::::::",
            "              \\                   __/::;::;;::::::       ::::::::::",
            "              |`                 /;;;;/::|  \\::::  :___:  :::::::::",
            "              \\                 |'_,::::/ \\ |:::: .|  |`. :::::::::",
            "              /                _/::::::/  / /:::: | \\.' | ::. .::::",
            "             |                /.::;;:-'_)/_/::::: `._| .' ::..::::",
            "  ----.__    |               |.::|   \\___/:::::::         ::..::::",
            "  :::::::`----\\_____          \\:::\\.-'::::::::::: ___:___ ::..::::",
            "  ;;;;;:::::::::::::`------     \\:::::::::::::::: ___|___ ::..::::",
            "       `-------:::::::\\         /::::::::::::::::  __|    ::..::::",
            "  ___.--------'::::::::\\       |::::::::::::::::: |  |-.  ::..::::",
            "  :::::;;;:--:::::::::::|      /:::::::::::::::::  --'  ' ::..::::",
            "  ----'  _,-:.:::::::::::\\    |.:::::::::::::::::         ::..::::",
            "      __/.::::::::::::::::|   |.:::::::::::::::::  :   :  ::..::::",
            "   __/.:::;;::::::::;/.:::|   |.:::::::::::::::::  |    : ::. .::::",
            "  /.::::;/ /.:::::;/ |.::::|   \\_.:::::::::::::::  | .  | :::::::::",
            "  :::::/  /.:::::/  /.:::::|     \\__.::::::::::::  `.'  ' :::::::::",
            "  ::::|  |.:::::/  /.:::::.|        \\,::::::::::::       ::::::::::",
            "  ::::|  |.::::|   |.:::::/|       __/::::::::::::::::::::::::::::",
            "  \\.:::\\  \\.:::|   |.::::||| __.--:::::::::::::::::::::::::::::",
            "   \\.:::\\_ \\.:::\\   \\.:::'/.:::::::::::::::::::::::::::::::::",
            "    \\.::::\\ \\.:::\\   \\.::::::::::::::::::::::::::::::::::::::::::"
        };

        float boxW = Mathf.Clamp(windowWidth * 0.45f, 300f, 500f);
        float boxH = lines.Length * lineHeight + 16;
        float boxX = windowWidth - boxW - 10;
        float boxY = windowHeight - boxH - 10;

        var bgColor = new Color(0.12f, 0.12f, 0.16f, 0.9f);
        EditorGUI.DrawRect(new Rect(boxX, boxY, boxW, boxH), bgColor);

        var borderColor = new Color(0f, 0.75f, 1f, 0.4f);
        EditorGUI.DrawRect(new Rect(boxX, boxY, boxW, 1), borderColor);
        EditorGUI.DrawRect(new Rect(boxX, boxY + boxH - 1, boxW, 1), borderColor);
        EditorGUI.DrawRect(new Rect(boxX, boxY, 1, boxH), borderColor);
        EditorGUI.DrawRect(new Rect(boxX + boxW - 1, boxY, 1, boxH), borderColor);

        GUIStyle artStyle = new GUIStyle(EditorStyles.label)
        {
            alignment = TextAnchor.MiddleCenter,
            fontSize = fontSize,
            richText = false,
            wordWrap = false,
            clipping = TextClipping.Overflow,
        };
        artStyle.normal.textColor = new Color(0f, 1f, 1f);

        float y = boxY + 8;
        for (int i = 0; i < lines.Length; i++)
        {
            var lineRect = new Rect(boxX + 4, y, boxW - 8, lineHeight);
            GUI.Label(lineRect, lines[i], artStyle);
            y += lineHeight;
        }
    }

    private void DrawProjectSettings()
    {
        EditorGUILayout.BeginVertical("box");
        EditorGUILayout.LabelField("路径设置", EditorStyles.boldLabel);

        EditorGUILayout.BeginHorizontal();
        string configPath = ConfigLinkDatabase.GetConfigFolderPath();
        EditorGUILayout.LabelField("Config目录:", GUILayout.Width(80));
        EditorGUILayout.LabelField(configPath, EditorStyles.textField, GUILayout.ExpandWidth(true));
        if (GUILayout.Button("选择", GUILayout.Width(60)))
        {
            string selected = EditorUtility.OpenFolderPanel("选择配置导出目录", Application.dataPath, "");
            if (!string.IsNullOrEmpty(selected))
            {
                ConfigLinkDatabase.SetConfigFolderPath(selected);
            }
        }
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.EndVertical();
    }

    private void DrawTableSelector(float windowWidth, float windowHeight)
    {
        EditorGUILayout.BeginVertical("box", GUILayout.Height(windowHeight - 60));

        EditorGUILayout.BeginHorizontal();
        searchFilter = EditorGUILayout.TextField("搜索:", searchFilter, EditorStyles.toolbarSearchField);
        showOnlyExisting = EditorGUILayout.ToggleLeft("只显示当前项目存在的表", showOnlyExisting);
        EditorGUILayout.EndHorizontal();

        var allTables = showOnlyExisting ? ConfigLinkDatabase.GetExistingTables() : ConfigLinkDatabase.GetAllTableInfo();
        var filteredTables = string.IsNullOrEmpty(searchFilter)
            ? allTables
            : allTables.Where(t => t.displayName.Contains(searchFilter) ||
                                   t.tableName.Contains(searchFilter) ||
                                   t.description.Contains(searchFilter)).ToList();

        var displayNames = filteredTables.Select(x =>
        {
            string existMark = showOnlyExisting ? "" : (x.isExistInProject ? "[存在]" : "[缺失]");
            return $"{x.displayName} ({x.tableName}) {existMark}";
        }).ToArray();

        var tableKeys = filteredTables.Select(x => x.tableName).ToArray();

        EditorGUILayout.Space();

        float innerHeight = windowHeight - 80; // 减去头部和边距

        EditorGUILayout.BeginHorizontal(GUILayout.Height(innerHeight));

        float leftPanelWidth = Mathf.Clamp(windowWidth * 0.3f, 180f, 300f);
        float rightPanelWidth = windowWidth - leftPanelWidth - 20f;

        EditorGUILayout.BeginVertical(GUILayout.Width(leftPanelWidth));
        EditorGUILayout.LabelField($"配置表列表 ({filteredTables.Count}):", EditorStyles.miniBoldLabel);
        float scrollHeight = innerHeight - 40; // 减去标签高度和边距
        relationScrollPos = EditorGUILayout.BeginScrollView(relationScrollPos, false, true, GUILayout.Height(scrollHeight));

        GUIStyle listButtonStyle = new GUIStyle(EditorStyles.label) { wordWrap = false, stretchWidth = true };

        for (int i = 0; i < displayNames.Length; i++)
        {
            bool isSelected = tableKeys[i] == selectedTableName;
            GUIStyle buttonStyle = isSelected ? "Button" : listButtonStyle;

            if (GUILayout.Button(displayNames[i], buttonStyle))
            {
                if (selectedTableName != tableKeys[i])
                {
                    selectedTableName = tableKeys[i];
                    expandedRelations.Clear();
                }
            }
        }

        EditorGUILayout.EndScrollView();
        EditorGUILayout.EndVertical();

        EditorGUILayout.BeginVertical(GUILayout.Width(rightPanelWidth));
        EditorGUILayout.LabelField("当前表详情:", EditorStyles.miniBoldLabel);
        detailScrollPos = EditorGUILayout.BeginScrollView(detailScrollPos, false, true, GUILayout.Height(scrollHeight));
        DrawCurrentTableInfo(rightPanelWidth);
        EditorGUILayout.EndScrollView();
        EditorGUILayout.EndVertical();

        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
    }

    private void DrawCurrentTableInfo(float panelWidth)
    {
        if (string.IsNullOrEmpty(selectedTableName))
        {
            EditorGUILayout.LabelField("请从左侧选择一个配置表", EditorStyles.centeredGreyMiniLabel);
            return;
        }

        var tableInfo = ConfigLinkDatabase.GetTableInfo(selectedTableName);
        if (tableInfo == null)
        {
            EditorGUILayout.LabelField("未找到该表的配置信息", EditorStyles.helpBox);
            return;
        }

        EditorGUILayout.LabelField($"当前表: {tableInfo.displayName}", EditorStyles.boldLabel);
        EditorGUILayout.LabelField($"表名: {tableInfo.tableName}", EditorStyles.miniLabel);
        EditorGUILayout.LabelField($"描述: {tableInfo.description}", EditorStyles.miniLabel);
        EditorGUILayout.LabelField($"状态: {(tableInfo.isExistInProject ? "存在于当前项目" : "当前项目不存在")}",
            tableInfo.isExistInProject ? EditorStyles.miniLabel : EditorStyles.helpBox);

        EditorGUILayout.Space();

        DrawOpenExcelButtons(panelWidth);

        EditorGUILayout.Space();

        showReverseRelations = EditorGUILayout.ToggleLeft("显示反向引用", showReverseRelations);

        EditorGUILayout.Space();

        if (showReverseRelations)
        {
            DrawReverseRelations(panelWidth);
        }
        else
        {
            if (tableInfo.relations.Count == 0)
            {
                EditorGUILayout.LabelField("该表暂无联动关系", EditorStyles.miniLabel);
            }
            else
            {
                EditorGUILayout.LabelField($"联动关系 ({tableInfo.relations.Count}):", EditorStyles.boldLabel);

                foreach (var relation in tableInfo.relations)
                {
                    DrawRelation(relation, panelWidth);
                }
            }
        }
    }

    private void DrawOpenExcelButtons(float panelWidth)
    {
        EditorGUILayout.BeginHorizontal();

        string excelPath = ConfigLinkDatabase.GetExcelFilePath(selectedTableName);
        bool canOpen = !string.IsNullOrEmpty(excelPath);

        if (canOpen)
        {
            if (GUILayout.Button("打开表格", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5)))
            {
                OpenExcelFile(selectedTableName);
            }
        }
        else
        {
            EditorGUILayout.LabelField("未找到Excel文件", EditorStyles.helpBox, GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5));
        }

        if (GUILayout.Button("批量打开关联表", GUILayout.Height(30), GUILayout.Width(panelWidth / 2 - 5)))
        {
            OpenRelatedTables(selectedTableName);
        }

        EditorGUILayout.EndHorizontal();
    }

    private void OpenExcelFile(string tableName)
    {
        string excelPath = ConfigLinkDatabase.GetExcelFilePath(tableName);
        if (!string.IsNullOrEmpty(excelPath) && File.Exists(excelPath))
        {
            System.Diagnostics.Process.Start(excelPath);
        }
    }

    private void OpenRelatedTables(string tableName)
    {
        var relatedTables = ConfigLinkDatabase.GetRelatedTableNames(tableName);
        foreach (var relatedTable in relatedTables)
        {
            OpenExcelFile(relatedTable);
        }
    }

    private void DrawReverseRelations(float panelWidth)
    {
        var reverseRels = ConfigLinkDatabase.GetReverseRelations(selectedTableName);
        if (reverseRels.Count == 0)
        {
            EditorGUILayout.LabelField("暂无其他表引用该表", EditorStyles.miniLabel);
        }
        else
        {
            EditorGUILayout.LabelField($"反向引用 ({reverseRels.Count}):", EditorStyles.boldLabel);

            foreach (var rel in reverseRels)
            {
                bool sourceExist = ConfigLinkDatabase.IsTableExist(rel.sourceTable);
                EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10));

                GUIStyle headerStyle = new GUIStyle(sourceExist ? EditorStyles.boldLabel : EditorStyles.helpBox) { wordWrap = false };
                EditorGUILayout.LabelField($"{rel.sourceDisplayName} ({rel.sourceTable}) → {rel.fieldName}", headerStyle);

                GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false };
                EditorGUILayout.LabelField($"说明: {rel.description}", labelStyle);

                if (!string.IsNullOrEmpty(rel.relationFormat))
                {
                    EditorGUILayout.LabelField($"格式: {rel.relationFormat}", labelStyle);
                }

                if (sourceExist && GUILayout.Button($"查看 {rel.sourceTable} 表", EditorStyles.miniButton))
                {
                    selectedTableName = rel.sourceTable;
                    expandedRelations.Clear();
                }

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

    private void DrawExcelPathSettings(float windowWidth)
    {
        EditorGUILayout.BeginVertical("box");
        EditorGUILayout.LabelField("Excel 文件夹", EditorStyles.boldLabel);

        string currentPath = ConfigLinkDatabase.GetExcelFolderPath();
        string displayPath = string.IsNullOrEmpty(currentPath) ? "未设置" : currentPath;

        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.LabelField("路径:", GUILayout.Width(36));
        EditorGUILayout.LabelField(displayPath, EditorStyles.textField, GUILayout.ExpandWidth(true));

        if (GUILayout.Button("选择", GUILayout.Width(60)))
        {
            string selectedPath = EditorUtility.OpenFolderPanel("选择Excel文件夹", "", "");
            if (!string.IsNullOrEmpty(selectedPath))
            {
                ConfigLinkDatabase.SetExcelFolderPath(selectedPath);
            }
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.EndVertical();
    }

    private void DrawRelation(FieldRelation relation, float panelWidth)
    {
        string key = $"{selectedTableName}_{relation.fieldName}_{relation.targetTable}";
        if (!expandedRelations.ContainsKey(key))
        {
            expandedRelations[key] = false;
        }

        bool targetExist = ConfigLinkDatabase.IsTableExist(relation.targetTable);
        string existMark = targetExist ? "" : "[目标表缺失]";

        EditorGUILayout.BeginVertical("frameBox", GUILayout.Width(panelWidth - 10));

        GUIStyle foldoutStyle = new GUIStyle(EditorStyles.foldout) { wordWrap = false, richText = true };
        expandedRelations[key] = EditorGUILayout.Foldout(expandedRelations[key],
            $"{relation.fieldName} → {relation.targetTable}.{relation.targetField} {existMark}",
            true, foldoutStyle);

        if (expandedRelations[key])
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.Space();
            EditorGUILayout.BeginVertical("helpBox", GUILayout.Width(panelWidth - 30));

            GUIStyle labelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = false, richText = true };

            EditorGUILayout.LabelField($"目标表: {relation.targetTable} {(targetExist ? "" : "(当前项目不存在)")}", labelStyle);
            EditorGUILayout.LabelField($"目标字段: {relation.targetField}", labelStyle);

            if (!string.IsNullOrEmpty(relation.relationFormat))
            {
                EditorGUILayout.LabelField($"格式: {relation.relationFormat}", labelStyle);
                DrawFormatExample(relation.relationFormat);
            }

            EditorGUILayout.LabelField($"说明: {relation.description}", labelStyle);

            if (targetExist && GUILayout.Button($"查看 {relation.targetTable} 表", EditorStyles.miniButton))
            {
                selectedTableName = relation.targetTable;
                expandedRelations.Clear();
            }

            EditorGUILayout.EndVertical();
            EditorGUILayout.EndHorizontal();
        }

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

    private void DrawFormatExample(string format)
    {
        string example = ConfigLinkDatabase.FormatRelationValue(format, "1_2_3_4");
        if (example != "1_2_3_4")
        {
            GUIStyle exampleStyle = new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic };
            EditorGUILayout.LabelField($"示例: {example}", exampleStyle);
        }
    }

    private static void RunAIConfigScript()
    {
        string scriptPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "generate_config_link_data.py");
        if (!File.Exists(scriptPath))
        {
            EditorUtility.DisplayDialog("提示", "未找到脚本文件:\n" + scriptPath, "确定");
            return;
        }

        string pythonExe = FindPythonExecutable();
        if (string.IsNullOrEmpty(pythonExe))
        {
            EditorUtility.DisplayDialog("提示", "未找到 Python 环境，请确保已安装 Python 并添加到 PATH", "确定");
            return;
        }

        try
        {
            string configDir = ConfigLinkDatabase.GetConfigFolderPath();
            string tableInfoPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "table_info.json");
            string outputPath = Path.Combine(Application.dataPath, "Editor/ConfigLinkViewer", "ConfigLinkData.json");

            string arguments = $"\"{scriptPath}\" --config-dir \"{Path.Combine(Application.dataPath, configDir)}\" --output \"{outputPath}\"";
            if (File.Exists(tableInfoPath))
            {
                arguments += $" --table-info \"{tableInfoPath}\"";
            }

            var startInfo = new System.Diagnostics.ProcessStartInfo
            {
                FileName = pythonExe,
                Arguments = arguments,
                WorkingDirectory = Path.GetDirectoryName(scriptPath),
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                CreateNoWindow = true,
                StandardOutputEncoding = System.Text.Encoding.UTF8,
                StandardErrorEncoding = System.Text.Encoding.UTF8,
            };

            using (var process = System.Diagnostics.Process.Start(startInfo))
            {
                string stdout = process.StandardOutput.ReadToEnd();
                string stderr = process.StandardError.ReadToEnd();
                process.WaitForExit();

                if (process.ExitCode == 0)
                {
                    Debug.Log("[ConfigLinkViewer] 补全配置完成:\n" + stdout);
                    ConfigLinkDatabase.ClearCache();
                    AssetDatabase.Refresh();
                    EditorUtility.DisplayDialog("完成", "补全配置已完成\n\n" + stdout, "确定");
                }
                else
                {
                    Debug.LogError("[ConfigLinkViewer] 补全配置失败:\n" + stderr);
                    EditorUtility.DisplayDialog("失败", "脚本执行失败:\n" + stderr, "确定");
                }
            }
        }
        catch (Exception ex)
        {
            Debug.LogError("[ConfigLinkViewer] 运行脚本异常: " + ex.Message);
            EditorUtility.DisplayDialog("异常", "运行脚本时发生异常:\n" + ex.Message, "确定");
        }
    }

    private static string FindPythonExecutable()
    {
        string[] candidates = { "python", "python3", "py" };
        foreach (var candidate in candidates)
        {
            try
            {
                var startInfo = new System.Diagnostics.ProcessStartInfo
                {
                    FileName = candidate,
                    Arguments = "--version",
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                };
                using (var process = System.Diagnostics.Process.Start(startInfo))
                {
                    process.WaitForExit();
                    if (process.ExitCode == 0)
                        return candidate;
                }
            }
            catch { }
        }
        return null;
    }

    private static void CleanExcelEmptyRows()
    {
        string excelFolder = ConfigLinkDatabase.GetExcelFolderPath();
        if (string.IsNullOrEmpty(excelFolder))
        {
            if (ConfigLinkViewerCallbacks.FallbackExcelPathCallback != null)
            {
                excelFolder = ConfigLinkViewerCallbacks.FallbackExcelPathCallback();
            }
        }

        if (string.IsNullOrEmpty(excelFolder) || !Directory.Exists(excelFolder))
        {
            EditorUtility.DisplayDialog("提示",
                "Excel文件夹路径未设置或不存在。\n请切换到[配置]标签页设置，或在 ConfigLinkViewerCallbacks.FallbackExcelPathCallback 中注册回退逻辑。",
                "确定");
            return;
        }

        var files = Directory.GetFiles(excelFolder, "*.xlsx", SearchOption.AllDirectories)
            .Where(f => !Path.GetFileName(f).StartsWith("~$"))
            .ToArray();

        if (files.Length == 0)
        {
            EditorUtility.DisplayDialog("提示", "未找到Excel文件", "确定");
            return;
        }

        int totalCleaned = 0;
        int cleanedFileCount = 0;

        try
        {
            foreach (var file in files)
            {
                try
                {
                    int removedCount = CleanSingleExcelFile(file);
                    if (removedCount > 0)
                    {
                        totalCleaned += removedCount;
                        cleanedFileCount++;
                    }
                }
                catch (Exception ex)
                {
                    Debug.LogError($"[清理空行] 处理文件失败: {Path.GetFileName(file)}, 错误: {ex.Message}");
                }
            }
        }
        finally
        {
            AssetDatabase.Refresh();
        }

        if (cleanedFileCount > 0)
        {
            EditorUtility.DisplayDialog("清理完成",
                $"扫描 {files.Length} 个文件\n清理了 {cleanedFileCount} 个文件中共 {totalCleaned} 行空数据", "确定");
        }
        else
        {
            EditorUtility.DisplayDialog("清理完成",
                $"扫描 {files.Length} 个文件，未发现需要清理的空行", "确定");
        }
    }

    private static int CleanSingleExcelFile(string filePath)
    {
        const int headerRowCount = 3;
        XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";

        XDocument doc;
        using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Read))
        {
            var sheetEntry = archive.GetEntry("xl/worksheets/sheet1.xml");
            if (sheetEntry == null) return 0;
            using (var stream = sheetEntry.Open())
            {
                doc = XDocument.Load(stream);
            }
        }

        List<string> sharedStrings = null;
        using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Read))
        {
            var ssEntry = archive.GetEntry("xl/sharedStrings.xml");
            if (ssEntry != null)
            {
                using (var stream = ssEntry.Open())
                {
                    var ssDoc = XDocument.Load(stream);
                    sharedStrings = ssDoc.Root.Elements(ns + "si")
                        .Select(si => si.Value)
                        .ToList();
                }
            }
        }

        var sheetData = doc.Root.Element(ns + "sheetData");
        if (sheetData == null) return 0;

        var rows = sheetData.Elements(ns + "row").ToList();
        if (rows.Count <= headerRowCount) return 0;

        var emptyRows = new List<XElement>();

        for (int i = headerRowCount; i < rows.Count; i++)
        {
            var row = rows[i];
            var cells = row.Elements(ns + "c").ToList();

            if (cells.Count == 0)
            {
                emptyRows.Add(row);
                continue;
            }

            bool allEmpty = true;
            foreach (var cell in cells)
            {
                var value = cell.Element(ns + "v");
                if (value != null && !string.IsNullOrWhiteSpace(value.Value))
                {
                    allEmpty = false;
                    break;
                }

                var cellType = cell.Attribute("t")?.Value;
                if (cellType == "s" && value != null)
                {
                    if (int.TryParse(value.Value, out int ssIdx) && sharedStrings != null && ssIdx < sharedStrings.Count)
                    {
                        if (!string.IsNullOrWhiteSpace(sharedStrings[ssIdx]))
                        {
                            allEmpty = false;
                            break;
                        }
                    }
                }
            }

            if (allEmpty)
            {
                emptyRows.Add(row);
            }
        }

        if (emptyRows.Count == 0) return 0;

        foreach (var row in emptyRows)
        {
            row.Remove();
        }

        using (var archive = ZipFile.Open(filePath, ZipArchiveMode.Update))
        {
            var sheetEntry = archive.GetEntry("xl/worksheets/sheet1.xml");
            if (sheetEntry != null)
            {
                sheetEntry.Delete();
                var newEntry = archive.CreateEntry("xl/worksheets/sheet1.xml");
                using (var stream = newEntry.Open())
                {
                    doc.Save(stream);
                }
            }
        }

        return emptyRows.Count;
    }
}
