using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using FairyView;
using StandardScene.Model;
using Simple3;
using SimpleCore;
using SimpleCore.Library;
using StandardScene.Utils;
using static StandardScene.Chained.ChainedDeliveryMission;

namespace StandardScene.Chained
{
    /// <summary>
    /// 搬运任务管理界面（CycleGUI 版，替代原 WinForms <c>DeliveryViewer</c> 窗体）。
    /// <list type="bullet">
    /// <item>单实例：再次打开则把已有面板置前。</item>
    /// <item>约每 1s 节流刷新任务快照（在渲染线程内节流，避免并发），面板 500ms 准实时重绘。</item>
    /// <item>每行提供「取消 / 重发 / 换车重发」按钮（带二次确认），超时任务整行高亮。</item>
    /// </list>
    /// 保留可实例化 + <see cref="Show"/> 以兼容既有调用 <c>new DeliveryViewer().Show()</c>。
    /// </summary>
    public class DeliveryViewer
    {
        private const int OverdueMinutesThreshold = 10000; // 约 7 天视为超时
        private const string TableId = "delivery-task-list";

        private readonly HttpClient SharedHttpClient = new HttpClient();
        /// <summary>超时任务整行底色（深色主题下的暗红，醒目但不刺眼）。</summary>
        private readonly Color OverdueRowColor = Color.FromArgb(255, 90, 36, 36);

        private Panel _panel;
        private bool _showFinished = true;   // 显示已完成任务
        private bool _showAbolished = true;  // 显示废止任务（Error / Canceled / Terminated）

        // 渲染快照：由后台线程按 FlushInterval 刷新，渲染线程只读引用；锁/文件 IO 绝不放在渲染线程，避免界面卡死。
        private volatile List<Delivery> _snapshot = new List<Delivery>();
        private volatile bool _refreshing;
        private DateTime _lastFlush = DateTime.MinValue;
        private readonly TimeSpan FlushInterval = TimeSpan.FromSeconds(1);
        private volatile string _status = "";

        // 显示筛选（仅作用于已拉取的快照，渲染线程内即时过滤，不触发重新拉取）
        private string _filterSrc = "";
        private string _filterDst = "";
        private string _filterCar = "";
        private int _filterStatusIdx;
        private readonly string[] _statusFilterNames =
            new[] { "全部" }.Concat(Enum.GetNames(typeof(DeliveryStatus))).ToArray();

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

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

        private static DeliveryViewer _instance;

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

            var panel = GUI.DeclarePanel()
                .ShowTitle("任务列表")
                .ForceFloat()
                .InitSize(1500, 620) // 列宽按内容自适应(SizingFixedFit)，给足初始宽度避免 10 列横向拥挤
                .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.CheckBox("显示已完成任务", ref _showFinished)) _lastFlush = DateTime.MinValue;
                pb.SameLine(16);
                if (pb.CheckBox("显示废止的任务（Error / Canceled / Terminated）", ref _showAbolished)) _lastFlush = DateTime.MinValue;

                EnsureSnapshotFresh();

                var items = _snapshot;

                // 筛选栏：起点 / 终点 / 车辆 / 任务状态（仅过滤当前快照，不触发重新拉取）
                // ###id：控件 id 经 ASCII 哈希，同字数纯中文标签（起点筛选/终点筛选）会撞 id，用 ### 后缀给唯一 ASCII id（显示不变）。
                var (fs, _) = pb.TextInput("起点筛选（站点ID或名称）###flt-src", _filterSrc, alwaysReturnString: true); _filterSrc = fs;
                var (fd, _) = pb.TextInput("终点筛选（站点ID或名称）###flt-dst", _filterDst, alwaysReturnString: true); _filterDst = fd;
                var (fc, _) = pb.TextInput("车辆筛选（车辆名）###flt-car", _filterCar, alwaysReturnString: true); _filterCar = fc;
                pb.DropdownBox("状态筛选###flt-status", _statusFilterNames, ref _filterStatusIdx);
                if (pb.Button("清空筛选", distinct: "delivery-clear-filter"))
                {
                    _filterSrc = _filterDst = _filterCar = "";
                    _filterStatusIdx = 0;
                }

                var filtered = ApplyFilters(items);
                pb.Label($"共 {filtered.Count}/{items.Count} 个任务（超时任务高亮置顶）");

                pb.Table(TableId,
                    new[] { "任务号", "小车", "取货点", "放货点", "任务状态", "下发时间", "执行时间", "结束时间", "优先级", "操作" },
                    filtered.Count, (row, i) =>
                    {
                        var dd = filtered[i];
                        if (IsOverdue(dd)) row.SetColor(OverdueRowColor);

                        row.Label($"{dd.Id}");
                        row.Label(dd.UsingCar?.name ?? "");
                        row.Label($"{dd.Src}-{SafeSiteName(dd.Src)}");
                        row.Label($"{dd.Dst}-{SafeSiteName(dd.Dst)}");
                        row.Label($"{dd.GetStatus()}");
                        row.Label($"{dd.CreateTime:yyyy-MM-dd HH:mm:ss}");
                        row.Label($"{dd.StartTime:yyyy-MM-dd HH:mm:ss}");
                        row.Label($"{dd.FinishTime:yyyy-MM-dd HH:mm:ss}");
                        row.Label($"{dd.Priority}");

                        var op = row.ButtonGroup(
                            new[] { "取消", "重发", "换车" },
                            new[] { "取消任务", "重发任务", "换车重发任务" });
                        var taskCode = dd.Id;
                        // 业务操作含文件 IO 与锁竞争，统一用 Task.Run 放后台执行，绝不阻塞渲染线程（否则界面卡死）。
                        if (op == 0) FairyUiHelper.ConfirmThen($"是否结束任务 {taskCode}？", () => Task.Run(() => CancelDelivery(taskCode)));
                        else if (op == 1) FairyUiHelper.ConfirmThen($"是否重发任务 {taskCode}？", () => Task.Run(() => ResendDelivery(taskCode)));
                        else if (op == 2) FairyUiHelper.ConfirmThen($"是否换车重发任务 {taskCode}？", () => Task.Run(() => ChangeCarResendDelivery(taskCode)));
                    }, height: FairyUiHelper.TableHeightPx(18), enableSearch: true);

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

                // 节流重绘：任务监控无需高帧率，约 500ms 刷新一次即可保持准实时，显著降低 CPU。
                pb.Panel.Repaint(repaintTimeMs: 500);
            });
        }

        private bool IsOverdue(Delivery dd) =>
            (DateTime.Now - dd.CreateTime).TotalMinutes > OverdueMinutesThreshold;

        /// <summary>按 起点 / 终点 / 车辆 / 任务状态 对当前快照即时过滤（空条件不限制；起点终点匹配“ID-名称”，大小写不敏感）。</summary>
        private List<Delivery> ApplyFilters(List<Delivery> items)
        {
            IEnumerable<Delivery> q = items;

            var fSrc = (_filterSrc ?? "").Trim();
            if (fSrc.Length > 0)
                q = q.Where(d => $"{d.Src}-{SafeSiteName(d.Src)}".Contains(fSrc, StringComparison.OrdinalIgnoreCase));

            var fDst = (_filterDst ?? "").Trim();
            if (fDst.Length > 0)
                q = q.Where(d => $"{d.Dst}-{SafeSiteName(d.Dst)}".Contains(fDst, StringComparison.OrdinalIgnoreCase));

            var fCar = (_filterCar ?? "").Trim();
            if (fCar.Length > 0)
                q = q.Where(d => (d.UsingCar?.name ?? "").Contains(fCar, StringComparison.OrdinalIgnoreCase));

            if (_filterStatusIdx > 0 && _filterStatusIdx < _statusFilterNames.Length)
            {
                var st = _statusFilterNames[_filterStatusIdx];
                q = q.Where(d => d.GetStatus().ToString() == st);
            }

            return q.ToList();
        }

        /// <summary>
        /// 渲染线程调用：到达刷新间隔且无在途刷新时，<b>在后台线程</b>重新拉取任务快照（超时任务置顶）。
        /// 业务侧的锁与文件 IO 一律放到后台，渲染线程只读 <see cref="_snapshot"/> 引用，避免界面卡死。
        /// </summary>
        private void EnsureSnapshotFresh()
        {
            if (_refreshing) return;
            if (DateTime.Now - _lastFlush < FlushInterval) return;
            _lastFlush = DateTime.Now;
            _refreshing = true;

            // 捕获当前过滤条件，避免后台读取过程中被 UI 改动。
            bool showFinished = _showFinished, showAbolished = _showAbolished;
            Task.Run(() =>
            {
                try
                {
                    var list = new List<Delivery>();
                    foreach (var cdm in SimpleProject.proj.Missions.OfType<ChainedDeliveryMission>())
                        list.AddRange(cdm.GetDeliveries(showFinished, showAbolished, showAbolished, showAbolished));

                    // 与原窗体一致：超时任务排在最前。
                    _snapshot = list.OrderByDescending(d => IsOverdue(d) ? 1 : 0).ToList();
                }
                catch (Exception ex)
                {
                    Diagnosis.Post($"DeliveryViewer 刷新异常: {ExceptionFormatter.FormatEx(ex)}");
                }
                finally
                {
                    _refreshing = false;
                }
            });
        }

        private string SafeSiteName(int siteId)
        {
            try { return SimpleLib.GetSite(siteId)?.name ?? ""; }
            catch { return ""; }
        }

        /// <summary>将任务标记为已取消（Canceled）。</summary>
        private void MarkDeliveryCanceled(Delivery d)
        {
            if (d == null) return;
            lock (d.SyncStatus)
            {
                d.Canceled = true;
                d.Active = false;
            }
        }

        /// <summary>
        /// 将任务状态重置为 Waiting。
        /// 当 clearCarForChange=true 时，仅当状态为 Suspended 或 Waiting 且未处于放货阶段时，
        /// 才会清空 UsingCar 并返回 true；否则返回 false。
        /// </summary>
        private bool MarkDeliveryWaiting(Delivery d, bool clearCarForChange)
        {
            if (d == null) return false;
            lock (d.SyncStatus)
            {
                var status = d.GetStatus();
                if (clearCarForChange)
                {
                    if ((status is not DeliveryStatus.Suspended and not DeliveryStatus.Waiting) || d.Putting)
                    {
                        return false;
                    }

                    d.UsingCar = null;
                }
                else
                {
                    d.SkipFetch = d.Putting;
                }

                d.Active = false;
                d.Finished = false;
                d.Error = false;
                d.Canceled = false;
                d.Terminated = false;
                d.Suspended = false;
                return true;
            }
        }

        private void ResendDelivery(string taskCode)
        {
            try
            {
                var cdm = SimpleProject.proj.Missions.OfType<TransportMission>().FirstOrDefault();
                if (cdm == null) return;
                var d = cdm.GetDeliveries(true, true, true, true)
                           .OfType<TransportDelivery>()
                           .FirstOrDefault(s => s.Id == taskCode);
                if (d == null)
                {
                    _status = $"重发失败：列表中不存在任务 {taskCode}";
                    return;
                }
                if (!MarkDeliveryWaiting(d, clearCarForChange: false))
                {
                    _status = "重发任务失败：当前状态不允许重发";
                    return;
                }

                cdm.PersistDelivery(d);
                _status = $"已重发任务 {taskCode}";
            }
            catch (Exception ex)
            {
                _status = $"重发任务 {taskCode} 异常，详见日志";
                Diagnosis.Post($"重发任务 {taskCode} 异常: {ExceptionFormatter.FormatEx(ex)}");
            }
        }

        private void CancelDelivery(string taskCode)
        {
            try
            {
                var cdm = SimpleProject.proj.Missions.OfType<TransportMission>().FirstOrDefault();
                if (cdm == null) return;
                var d = cdm.GetDeliveries(true, true, true, true)
                           .OfType<TransportDelivery>()
                           .FirstOrDefault(s => s.Id == taskCode);
                if (d == null)
                {
                    _status = $"取消失败：列表中不存在任务 {taskCode}";
                    return;
                }
                if (d.IsFinished()) return;

                // 1) 状态上将任务标记为已取消
                MarkDeliveryCanceled(d);

                // 2) 若小车当前正在执行该任务，则下发 reset 指令
                bool excutingTask = d.UsingCar != null && d.UsingCar.tags.IsEqual("taskCode", d.TaskId);
                if (excutingTask && d.UsingCar != null
                    && (d.UsingCar.tags?.Contains("occupied") == true || (d.UsingCar.status?.pendingLocks?.Length ?? 0) != 0))
                {
                    _ = SharedHttpClient.GetStringAsync($"http://{d.UsingCar.address}:8008/reset");
                    Diagnosis.Log($"手动结束任务;{d.TaskId}", "task", true);
                }

                // 3) 持久化已取消状态
                cdm.PersistDelivery(d);
                _status = $"已结束任务 {taskCode}";
            }
            catch (Exception ex)
            {
                _status = $"结束任务 {taskCode} 异常，详见日志";
                Diagnosis.Post($"结束任务 {taskCode} 异常: {ExceptionFormatter.FormatEx(ex)}");
            }
        }

        private void ChangeCarResendDelivery(string taskCode)
        {
            try
            {
                var cdm = SimpleProject.proj.Missions.OfType<TransportMission>().FirstOrDefault();
                if (cdm == null) return;
                var d = cdm.GetDeliveries(true, true, true, true)
                           .OfType<TransportDelivery>()
                           .FirstOrDefault(s => s.Id == taskCode);
                if (d == null)
                {
                    _status = $"换车重发失败：列表中不存在任务 {taskCode}";
                    return;
                }

                if (!MarkDeliveryWaiting(d, clearCarForChange: true))
                {
                    _status = "换车重发失败：仅当任务状态为 Suspended 或 Waiting 且未处于放货阶段时才允许换车重发";
                    return;
                }

                cdm.PersistDelivery(d);
                _status = $"已换车重发任务 {taskCode}";
            }
            catch (Exception ex)
            {
                _status = $"换车重发任务 {taskCode} 异常，详见日志";
                Diagnosis.Post($"换车重发任务 {taskCode} 异常: {ExceptionFormatter.FormatEx(ex)}");
            }
        }
    }
}
