using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FairyView;
using Newtonsoft.Json;
using SimpleCore.Library;
using StandardScene.Model;
using StandardScene.Utils;

namespace LoopViewerApp
{
    /// <summary>
    /// 环线/循环任务配置管理界面（CycleGUI 版，替代原 WinForms <c>LoopViewer</c> 窗体）。
    /// <list type="bullet">
    /// <item>维护 <c>tasklist.json</c>（<see cref="List{T}"/> of <see cref="LoopTask"/>）的增 / 改 / 删；与 <c>AbstractLoopMission</c> 读取同一文件。</item>
    /// <item>单实例：再次打开则把已有面板置前。</item>
    /// <item>勾选多行后「删除选中」可批量删除（保留原 ListView 多选删除能力）；每行「编辑」按钮打开编辑对话框。</item>
    /// <item>文件写入放后台线程，绝不阻塞渲染线程（避免界面卡死）。</item>
    /// </list>
    /// 沿用 <c>DeliveryViewer</c> 的同套模式（单实例面板、<c>pb.Table</c>、<c>FairyUiHelper.ConfirmThen</c>），不另造轮子。
    /// 保留可实例化 + <see cref="Show"/> 以兼容既有调用 <c>new LoopViewer().Show()</c>。
    /// </summary>
    public class LoopViewer
    {
        private const string TableId = "loop-task-list";

        // 与 AbstractLoopMission 完全一致的读取路径，保证“写哪儿、它就读哪儿”。
        private string JsonPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tasklist.json");

        private readonly object SaveLock = new object();
        // 直接取自枚举，自动与 TaskKind / TaskStartType 保持同步（含 Charge），无需手写列表。
        private readonly string[] KindNames = Enum.GetNames(typeof(TaskKind));
        private readonly string[] StartTypeNames = Enum.GetNames(typeof(TaskStartType));

        private Panel _panel;
        private Panel _dialog;                                      // 新增/编辑对话框，限单实例
        private List<LoopTask> _tasks = new List<LoopTask>();       // 仅渲染线程读写
        private readonly HashSet<int> _selected = new HashSet<int>(); // 仅渲染线程读写，存被勾选任务的 Id
        private volatile string _status = "";

        /// <summary>打开（或置前）任务管理面板。兼容原 <c>new LoopViewer().Show()</c> 调用方式。</summary>
        public void Show() => Open();

        /// <summary>打开（或置前）任务管理面板。</summary>
        public static void Open() => (_instance ??= new LoopViewer()).OpenCore();

        private static LoopViewer _instance;

        private void OpenCore()
        {
            if (_panel != null)
            {
                try
                {
                    _panel.BringToFront();
                    return;
                }
                catch
                {
                    _panel = null;
                }
            }

            _selected.Clear();
            LoadTasks();

            var panel = GUI.DeclarePanel()
                .ShowTitle("任务列表管理器")
                .ForceFloat()
                .InitSize(1080, 620)
                .InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
            _panel = panel;
            panel.IfTerminalQuit(() => _panel = null);

            panel.Define(pb =>
            {
                if (pb.Closing())
                {
                    panel.Exit();
                    _panel = null;
                    return;
                }

                if (pb.Button("新增任务", distinct: "loop-add"))
                    OpenEditDialog(null);
                pb.SameLine(12);
                if (pb.Button("删除选中", distinct: "loop-del-selected"))
                    ConfirmDeleteSelected();
                pb.SameLine(16);
                pb.Label($"共 {_tasks.Count} 个任务，已选 {_selected.Count} 个");

                pb.Table(TableId,
                    new[] { "选择", "ID", "任务类别", "当前站点", "目标站点", "流量控制", "优先级", "途径点", "启动类型", "操作" },
                    _tasks.Count, (row, i) =>
                    {
                        var t = _tasks[i];
                        var id = t.Id;

                        var sel = _selected.Contains(id);
                        if (row.Checkbox(ref sel, "勾选以批量删除"))
                        {
                            if (sel) _selected.Add(id);
                            else _selected.Remove(id);
                        }

                        row.Label($"{t.Id}");
                        row.Label($"{t.Kind}");
                        row.Label($"{t.CurrentStationId}");
                        row.Label($"{t.TargetStationId}");
                        row.Label($"{t.TrafficControl}");
                        row.Label($"{t.Priority}");
                        row.Label(t.IsViaPoint ? "是" : "否");
                        row.Label($"{t.StartType}");

                        if (row.ButtonGroup(new[] { "编辑" }, new[] { "编辑该任务" }) == 0)
                            OpenEditDialog(t);
                    }, height: FairyUiHelper.TableHeightPx(18), enableSearch: true);

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

                // 事件驱动为主，配合较慢的节流重绘即可保证后台保存结果/状态及时反映。
                pb.Panel.Repaint(repaintTimeMs: 500);
            });
        }

        /// <summary>对选中项发起二次确认后删除（保留原多选删除的提示文案）。</summary>
        private void ConfirmDeleteSelected()
        {
            if (_selected.Count == 0)
            {
                _status = "未选择任何任务";
                _panel?.Repaint();
                return;
            }

            string prompt;
            if (_selected.Count == 1)
                prompt = $"确认删除任务 ID={_selected.First()}？";
            else
                prompt = $"确认删除所选 {_selected.Count} 个任务？";

            // 删除仅做内存列表增删（极快，可在渲染线程执行）；真正的文件写入在 SaveTasks 内部放后台线程。
            FairyUiHelper.ConfirmThen(prompt, DeleteSelected);
        }

        private void DeleteSelected()
        {
            var removed = _tasks.RemoveAll(t => _selected.Contains(t.Id));
            _selected.Clear();
            SaveTasks();
            _status = $"已删除 {removed} 个任务";
            _panel?.Repaint();
        }

        /// <summary>
        /// 打开「新增 / 编辑」对话框（置顶非模态、限单实例）。<paramref name="existing"/> 为 null 表示新增，否则编辑该任务（保留其 Id）。
        /// 每次打开都是全新面板：<c>defaultText</c> 能正确初始化，规避立即模式下文本框缓冲难以重置的问题。
        /// </summary>
        private void OpenEditDialog(LoopTask existing)
        {
            if (_dialog != null)
            {
                try { _dialog.BringToFront(); return; }
                catch { _dialog = null; }
            }

            bool isAdd = existing == null;

            int kindIdx = isAdd ? 0 : Math.Max(0, Array.IndexOf(KindNames, existing.Kind.ToString()));
            int startIdx = Math.Max(0, Array.IndexOf(StartTypeNames,
                (isAdd ? TaskStartType.AutoLoop : existing.StartType).ToString()));
            string curText = (isAdd ? 0 : existing.CurrentStationId).ToString();
            string tgtText = (isAdd ? 0 : existing.TargetStationId).ToString();
            string trafficText = (isAdd ? 0 : Clamp(existing.TrafficControl, 0, 1000)).ToString();
            string priText = (isAdd ? 1 : Clamp(existing.Priority, 0, 100)).ToString();
            bool via = !isAdd && existing.IsViaPoint;
            string err = "";

            // 不用 Modal：原生「模态弹窗 + 标题栏关闭X」的 EndPopup 配对 bug 会断言崩溃。
            // 也不用 TopMost：置顶视口带 NoAutoMerge，会让 DropdownBox 的下拉弹窗落到独立非置顶视口里、被对话框挡在后面（看不到选项）。
            // 故采用与 DeliveryViewer 相同的普通浮动面板（非模态、不停靠）：Begin/End 路径，X 关闭干净，下拉弹窗 z 序正常。
            var dlg = GUI.DeclarePanel()
                .ShowTitle(isAdd ? "新增任务" : $"编辑任务 ID: {existing.Id}")
                .ForceFloat()
                .InitSize(420, 380)
                .InitPos(false, 0, 0, 0.5f, 0.5f, 0.5f, 0.5f);
            _dialog = dlg;
            dlg.IfTerminalQuit(() => _dialog = null);
            dlg.Define(pb =>
            {
                if (pb.Closing())
                {
                    dlg.Exit();
                    _dialog = null;
                    return;
                }

                // 控件 id 由 ImHashStr(prompt) 经 Encoding.ASCII 计算：中文会被压成 '?'，导致同字数纯中文标签
                // （如“当前站点/目标站点”“任务类别/启动类型”）哈希相同而抛 Duplicated id。故各标签加唯一 ASCII 序号前缀以区分。
                pb.DropdownBox("1. 任务类别", KindNames, ref kindIdx);
                var (c, _) = pb.TextInput("2. 当前站点", curText, alwaysReturnString: true);
                curText = c;
                var (tg, _) = pb.TextInput("3. 目标站点", tgtText, alwaysReturnString: true);
                tgtText = tg;
                var (tf, _) = pb.TextInput("4. 流量控制 (0~1000)", trafficText, alwaysReturnString: true);
                trafficText = tf;
                var (pr, _) = pb.TextInput("5. 优先级 (0~100)", priText, alwaysReturnString: true);
                priText = pr;
                pb.CheckBox("6. 途径点", ref via);
                pb.DropdownBox("7. 启动类型", StartTypeNames, ref startIdx);

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

                pb.Separator();
                if (pb.Button("保存", distinct: "loop-edit-save"))
                {
                    if (!int.TryParse((curText ?? "").Trim(), out var cur)) { err = "当前站点需为整数"; return; }
                    if (!int.TryParse((tgtText ?? "").Trim(), out var tgt)) { err = "目标站点需为整数"; return; }
                    if (!TryParseClamp(trafficText, 0, 1000, out var traffic)) { err = "流量控制需为 0~1000 的整数"; return; }
                    if (!TryParseClamp(priText, 0, 100, out var pri)) { err = "优先级需为 0~100 的整数"; return; }

                    Enum.TryParse<TaskKind>(KindNames[kindIdx], out var kind);
                    Enum.TryParse<TaskStartType>(StartTypeNames[startIdx], out var st);

                    if (isAdd)
                    {
                        _tasks.Add(new LoopTask
                        {
                            Id = GetNextTaskId(),
                            Kind = kind,
                            CurrentStationId = cur,
                            TargetStationId = tgt,
                            TrafficControl = traffic,
                            Priority = pri,
                            IsViaPoint = via,
                            StartType = st
                        });
                        _status = "已新增任务";
                    }
                    else
                    {
                        existing.Kind = kind;
                        existing.CurrentStationId = cur;
                        existing.TargetStationId = tgt;
                        existing.TrafficControl = traffic;
                        existing.Priority = pri;
                        existing.IsViaPoint = via;
                        existing.StartType = st;
                        _status = $"已保存任务 ID={existing.Id}";
                    }

                    SaveTasks();
                    dlg.Exit();
                    _dialog = null;
                    _panel?.Repaint();
                }
                pb.SameLine(8);
                if (pb.Button("取消", distinct: "loop-edit-cancel"))
                {
                    dlg.Exit();
                    _dialog = null;
                }
            });
        }

        /// <summary>下一个可用任务 Id（当前最大 Id + 1，空表则为 1）。</summary>
        private int GetNextTaskId() => _tasks.Count == 0 ? 1 : _tasks.Max(t => t.Id) + 1;

        private void LoadTasks()
        {
            try
            {
                var path = JsonPath;
                if (!File.Exists(path))
                    File.WriteAllText(path, "[]");

                var text = File.ReadAllText(path);
                _tasks = JsonConvert.DeserializeObject<List<LoopTask>>(text) ?? new List<LoopTask>();
            }
            catch (Exception ex)
            {
                _tasks = new List<LoopTask>();
                _status = "加载 tasklist.json 失败，详见日志";
                Diagnosis.Post($"LoopViewer 加载 tasklist.json 异常: {ExceptionFormatter.FormatEx(ex)}");
            }
        }

        /// <summary>序列化在渲染线程完成（极快），文件写入放后台线程，避免阻塞渲染线程。</summary>
        private void SaveTasks()
        {
            string json;
            try
            {
                json = JsonConvert.SerializeObject(_tasks, Formatting.Indented);
            }
            catch (Exception ex)
            {
                _status = "保存失败，详见日志";
                Diagnosis.Post($"LoopViewer 序列化 tasklist.json 异常: {ExceptionFormatter.FormatEx(ex)}");
                return;
            }

            var path = JsonPath;
            Task.Run(() =>
            {
                try
                {
                    lock (SaveLock)
                        File.WriteAllText(path, json);
                }
                catch (Exception ex)
                {
                    _status = "保存失败，详见日志";
                    Diagnosis.Post($"LoopViewer 保存 tasklist.json 异常: {ExceptionFormatter.FormatEx(ex)}");
                    _panel?.Repaint();
                }
            });
        }

        private int Clamp(int v, int min, int max) => v < min ? min : (v > max ? max : v);

        private bool TryParseClamp(string s, int min, int max, out int value)
        {
            if (int.TryParse((s ?? "").Trim(), out value))
            {
                value = Clamp(value, min, max);
                return true;
            }
            value = min;
            return false;
        }
    }
}
