using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using CycleGUI;
using Newtonsoft.Json;
using StandardScene.Utils;

namespace StandardScene.Signal.Ui
{
    /// <summary>
    /// 按 Model 的 <see cref="DisplayNameAttribute"/> 反射生成可增删改列表（CycleGUI）。
    /// 跳过 <see cref="BrowsableAttribute.Browsable"/> = false 与 <see cref="JsonIgnoreAttribute"/>。
    /// </summary>
    public static class ModelListPanel<T> where T : class, new()
    {
        private static Panel _panel;
        private static List<T> _items = new List<T>();
        private static int _selected = -1;
        private static string _status = "";
        private static string _title = "";
        private static string _path = "";
        private static Func<T, string> _keySelector = _ => "";
        private static readonly Dictionary<string, string> Text = new Dictionary<string, string>();
        private static readonly Dictionary<string, bool> Bools = new Dictionary<string, bool>();
        private static readonly Dictionary<string, int> EnumIdx = new Dictionary<string, int>();

        private static PropertyInfo[] Props => _props ??= Discover();
        private static PropertyInfo[] _props;

        public static void Open(string title, string path, Func<T, string> keySelector)
        {
            if (_panel != null)
            {
                try { _panel.BringToFront(); return; }
                catch { _panel = null; }
            }

            _title = title;
            _path = path;
            _keySelector = keySelector ?? (_ => "");
            _items = SignalConfigStore.Load<T>(path);
            _selected = -1;
            _status = $"已加载 {_items.Count} 条 · {_path}";
            ClearEditor();

            var typeKey = typeof(T).Name;
            var panel = GUI.DeclarePanel()
                .ShowTitle(title)
                .SetDefaultDocking(Panel.Docking.None)
                .InitSize(1100, 720)
                .InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
            _panel = panel;
            panel.IfTerminalQuit(() => { if (_panel == panel) _panel = null; });

            panel.Define(pb =>
            {
                if (pb.Closing())
                {
                    SignalConfigStore.Save(_path, _items);
                    panel.Exit();
                    _panel = null;
                    return;
                }

                var headers = Props.Select(DisplayOf).Concat(new[] { "行操作" }).ToArray();
                pb.SeparatorText($"列表（{_items.Count}）");
                pb.Table($"model-list-{typeKey}", headers, _items.Count, (row, i) =>
                {
                    var item = _items[i];
                    foreach (var p in Props)
                        row.Label(FormatCell(p.GetValue(item)));
                    if (row.ButtonGroup(new[] { "选择" }, new[] { "选择该行" }) == 0)
                    {
                        _selected = i;
                        LoadEditor(item);
                    }
                }, height: 10, enableSearch: true);

                pb.SeparatorText(_selected >= 0 ? $"编辑第 {_selected + 1} 行" : "编辑（未选中则「添加」写入新行）");
                DrawEditor(pb);

                if (pb.Button("添加", distinct: $"ml-{typeKey}-add")) Add();
                pb.SameLine(8);
                if (pb.Button("保存当前行", distinct: $"ml-{typeKey}-save")) SaveRow();
                pb.SameLine(8);
                if (pb.Button("删除当前行", distinct: $"ml-{typeKey}-del")) ConfirmDelete();
                pb.SameLine(8);
                if (pb.Button("清空编辑区", distinct: $"ml-{typeKey}-clear"))
                {
                    _selected = -1;
                    ClearEditor();
                    _status = "已清空编辑区，可添加新行";
                }

                if (!string.IsNullOrEmpty(_status))
                {
                    pb.Separator();
                    pb.Label(_status);
                }
            });
        }

        private static PropertyInfo[] Discover()
        {
            return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)
                .Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
                .Where(p => p.GetCustomAttribute<JsonIgnoreAttribute>() == null)
                .OrderBy(p => p.MetadataToken)
                .ToArray();
        }

        private static string DisplayOf(PropertyInfo p) =>
            p.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? p.Name;

        private static string FormatCell(object value)
        {
            if (value == null) return "";
            if (value is bool b) return b ? "是" : "否";
            return Convert.ToString(value) ?? "";
        }

        private static void DrawEditor(PanelBuilder pb)
        {
            for (var i = 0; i < Props.Length; i++)
            {
                var p = Props[i];
                var label = $"{i + 1}. {DisplayOf(p)}";
                var type = p.PropertyType;
                if (type == typeof(bool))
                {
                    if (!Bools.ContainsKey(p.Name)) Bools[p.Name] = false;
                    var v = Bools[p.Name];
                    pb.CheckBox(label, ref v);
                    Bools[p.Name] = v;
                }
                else if (type.IsEnum)
                {
                    var names = Enum.GetNames(type);
                    if (names.Length == 0) continue;
                    if (!EnumIdx.ContainsKey(p.Name)) EnumIdx[p.Name] = 0;
                    var idx = EnumIdx[p.Name];
                    if (idx < 0 || idx >= names.Length) idx = 0;
                    pb.DropdownBox(label, names, ref idx);
                    EnumIdx[p.Name] = idx;
                }
                else
                {
                    if (!Text.ContainsKey(p.Name)) Text[p.Name] = "";
                    var (text, _) = pb.TextInput(label, Text[p.Name], alwaysReturnString: true);
                    Text[p.Name] = text ?? "";
                }
            }
        }

        private static void LoadEditor(T item)
        {
            foreach (var p in Props)
            {
                var value = p.GetValue(item);
                if (p.PropertyType == typeof(bool))
                    Bools[p.Name] = value is true;
                else if (p.PropertyType.IsEnum)
                {
                    var names = Enum.GetNames(p.PropertyType);
                    var name = value?.ToString() ?? names.FirstOrDefault() ?? "";
                    var idx = Array.IndexOf(names, name);
                    EnumIdx[p.Name] = idx >= 0 ? idx : 0;
                }
                else
                    Text[p.Name] = value == null ? "" : Convert.ToString(value) ?? "";
            }
        }

        private static void ClearEditor()
        {
            Text.Clear();
            Bools.Clear();
            EnumIdx.Clear();
            foreach (var p in Props)
            {
                if (p.PropertyType == typeof(bool))
                    Bools[p.Name] = false;
                else if (p.PropertyType.IsEnum)
                    EnumIdx[p.Name] = 0;
                else if (p.PropertyType == typeof(int) || p.PropertyType == typeof(byte) || p.PropertyType == typeof(ushort))
                    Text[p.Name] = "0";
                else
                    Text[p.Name] = DefaultText(p);
            }
        }

        private static string DefaultText(PropertyInfo p)
        {
            try
            {
                var fresh = new T();
                var v = p.GetValue(fresh);
                return v == null ? "" : Convert.ToString(v) ?? "";
            }
            catch
            {
                return "";
            }
        }

        private static bool TryBuild(out T item, out string err)
        {
            item = new T();
            err = "";
            foreach (var p in Props)
            {
                try
                {
                    object boxed;
                    if (p.PropertyType == typeof(bool))
                        boxed = Bools.TryGetValue(p.Name, out var b) && b;
                    else if (p.PropertyType.IsEnum)
                    {
                        var names = Enum.GetNames(p.PropertyType);
                        var idx = EnumIdx.TryGetValue(p.Name, out var e) ? e : 0;
                        if (idx < 0 || idx >= names.Length) idx = 0;
                        boxed = Enum.Parse(p.PropertyType, names[idx]);
                    }
                    else if (p.PropertyType == typeof(int))
                    {
                        var raw = Text.TryGetValue(p.Name, out var t) ? t : "0";
                        if (!int.TryParse(string.IsNullOrWhiteSpace(raw) ? "0" : raw.Trim(), out var n))
                        {
                            err = $"{DisplayOf(p)} 必须是整数";
                            return false;
                        }
                        boxed = n;
                    }
                    else if (p.PropertyType == typeof(ushort))
                    {
                        var raw = Text.TryGetValue(p.Name, out var t) ? t : "0";
                        if (!ushort.TryParse(string.IsNullOrWhiteSpace(raw) ? "0" : raw.Trim(), out var n))
                        {
                            err = $"{DisplayOf(p)} 必须是 0-65535";
                            return false;
                        }
                        boxed = n;
                    }
                    else if (p.PropertyType == typeof(byte))
                    {
                        var raw = Text.TryGetValue(p.Name, out var t) ? t : "0";
                        if (!byte.TryParse(string.IsNullOrWhiteSpace(raw) ? "0" : raw.Trim(), out var n))
                        {
                            err = $"{DisplayOf(p)} 必须是 0-255";
                            return false;
                        }
                        boxed = n;
                    }
                    else
                        boxed = Text.TryGetValue(p.Name, out var s) ? s ?? "" : "";
                    p.SetValue(item, boxed);
                }
                catch (Exception ex)
                {
                    err = $"{DisplayOf(p)} 写入失败：{ex.Message}";
                    return false;
                }
            }

            var key = (_keySelector(item) ?? "").Trim();
            if (string.IsNullOrEmpty(key))
            {
                err = "主键不能为空";
                return false;
            }

            return true;
        }

        private static void Add()
        {
            if (!TryBuild(out var item, out var err))
            {
                CycleUiHelper.Alert("错误", err);
                return;
            }

            var key = _keySelector(item);
            if (_items.Any(x => string.Equals(_keySelector(x), key, StringComparison.OrdinalIgnoreCase)))
            {
                CycleUiHelper.Alert("错误", $"主键「{key}」已存在");
                return;
            }

            _items.Add(item);
            _selected = _items.Count - 1;
            SignalConfigStore.Save(_path, _items);
            _status = $"已添加 {key}";
            _panel?.Repaint();
        }

        private static void SaveRow()
        {
            if (_selected < 0 || _selected >= _items.Count)
            {
                CycleUiHelper.Alert("提示", "请先选择要保存的行，或用「添加」写入新行");
                return;
            }

            if (!TryBuild(out var item, out var err))
            {
                CycleUiHelper.Alert("错误", err);
                return;
            }

            var key = _keySelector(item);
            if (_items.Where((_, i) => i != _selected)
                .Any(x => string.Equals(_keySelector(x), key, StringComparison.OrdinalIgnoreCase)))
            {
                CycleUiHelper.Alert("错误", $"主键「{key}」已存在");
                return;
            }

            _items[_selected] = item;
            SignalConfigStore.Save(_path, _items);
            _status = $"已保存 {key}";
            CycleUiHelper.Alert("提示", "保存成功");
            _panel?.Repaint();
        }

        private static void ConfirmDelete()
        {
            if (_selected < 0 || _selected >= _items.Count)
            {
                CycleUiHelper.Alert("提示", "请先选择要删除的行");
                return;
            }

            var key = _keySelector(_items[_selected]);
            CycleUiHelper.ConfirmThen($"删除「{key}」？", () =>
            {
                _items.RemoveAt(_selected);
                _selected = -1;
                ClearEditor();
                SignalConfigStore.Save(_path, _items);
                _status = $"已删除 {key}";
                _panel?.Repaint();
            });
        }
    }
}
