using Newtonsoft.Json;
using SimpleLite;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleLite.RCS.Signal;
using SimpleLite.CADTools;
using SimpleLite.Props;
using SimpleLite.UI;
using SimpleCore;
using SimpleCore.Compiler;
using SimpleCore.Library;
using SimpleCore.PropType;
using StandardScene.Model;
using StandardScene;
using StandardScene.Utils;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;

namespace StandardScene.Chained
{
    /// <summary>
    /// 任务类型枚举
    /// </summary>
    public enum TaskType
    {
        移动 = 1, // 移动任务（当前未使用）
        搬运 = 2, // 搬运任务
        装车 = 3, // 装车任务
        卸车 = 4, // 卸车任务
    }

    /// <summary>
    /// 运输任务类
    /// 继承自AbstractDelivery，实现具体的运输任务功能
    /// </summary>
    /// public class TransportDelivery : AbstractChainedDeliveryMission.AbstractDelivery
    public class TransportDelivery : ChainedDeliveryMission.Delivery
    {
        /// <summary>任务类型（枚举值）</summary>
        public TaskType Type;
        /// <summary>任务类型（字符串形式）</summary>
        public string TaskType;
        /// <summary>使用的小车名称</summary>
        public string UsingCarName = "/";
        /// <summary>物料信息</summary>
        public string Material = "";
        /// <summary>占车站点</summary>
        public string HoldCarSite = "";
        /// <summary>阶段任务代码（用于标记阶段任务）</summary>
        public string MGTaskCode = "";
        /// <summary>任务ID字符串（用于标记阶段任务，可包含多个任务ID，用逗号分隔）</summary>
        public string TaskIdsString = "";

        /// <summary>
        /// 构造函数
        /// 注册任务生命周期回调函数
        /// </summary>
        public TransportDelivery()
        {
            // 注册任务开始回调
            OnStart += d => { TransportOnStart((TransportDelivery)d); };
            // 注册取货完成回调
            DoneFetch += d => { TransportDoneFetch((TransportDelivery)d); };
            // 注册放货完成回调
            DonePut += d => { TransportDonePut((TransportDelivery)d); };
            // 注册任务完成回调
            DoneMission += d => { TransportDoneMission((TransportDelivery)d); };
        }

        /// <summary>
        /// 任务开始时的回调处理
        /// </summary>
        /// <param name="d">运输任务对象</param>
        private void TransportOnStart(TransportDelivery d)
        {
            Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-onStart");
        }

        /// <summary>
        /// 取货完成时的回调处理
        /// </summary>
        /// <param name="d">运输任务对象</param>
        private void TransportDoneFetch(TransportDelivery d)
        {
            Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-doneFetch");
        }

        /// <summary>
        /// 放货完成时的回调处理
        /// </summary>
        /// <param name="d">运输任务对象</param>
        private void TransportDonePut(TransportDelivery d)
        {
            Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-donePut");
        }


        /// <summary>
        /// 任务完成时的回调处理
        /// </summary>
        /// <param name="d">运输任务对象</param>
        private void TransportDoneMission(TransportDelivery d)
        {
            Diagnosis.Post($"{d.TaskId}___{d.Src}->{d.Dst}: step-doneMission");
        }
    }

    /// <summary>
    /// 搬运任务进程类
    /// 继承自AbstractChainedDeliveryMission，实现具体的搬运任务调度
    /// </summary>
    [MissionType(Name = "搬运任务进程", editor = typeof(TransportMission))]
    internal class TransportMission : ChainedDeliveryMission
    {
        /// <summary>是否启用就近任务策略（"1"=启用，"0"=禁用）</summary>
        private static readonly string NearestTask = "1";

        /// <summary>任务查看器窗口对象</summary>
        private DeliveryViewer dv;

        public TransportMission()
        {
            // 确保回调静态类已完成注册
            TransportDeliveryCallbacks.EnsureInitialized();

            // 根据配置的 MissionCallbackURL 设置回调地址（若有）
            var p = Param;
            if (p != null && !string.IsNullOrWhiteSpace(p.MissionCallbackURL))
            {
                if (Commons.IsValidHttpUrl(p.MissionCallbackURL))
                {
                    _lastCallbackUrl = p.MissionCallbackURL;
                    TransportDeliveryCallbacks.ConfigureCallbackUrl(_lastCallbackUrl);
                }
                else
                {
                    Diagnosis.Log($"无效的 MissionCallbackURL 配置: {p.MissionCallbackURL}", "TransportMission");
                }
            }
        }

        public class TransportMissionParam
        {
            /// <summary>
            /// 递送任务状态回调地址。
            /// </summary>
            public string MissionCallbackURL;
        }

        public TransportMissionParam Param => StringDictConvert<TransportMissionParam>.Convert(fields);

        private readonly bool _onDisplay = true;
        private string _lastCallbackUrl;

        protected override Delivery CreateDeliveryFromSnapshot(DeliveryStateSnapshot snap)
        {
            var usingCar = SimpleLib.GetCar(snap.UsingCarId);
            if (snap.UsingCarId != -1 && usingCar == null)
            {
                Diagnosis.Post($"CreateDeliveryFromSnapshot[{snap.Id}] failed:usingCar[{snap.UsingCarId}] not found");
                return null;
            }
            if (usingCar != null && usingCar is not Car)
            {
                Diagnosis.Post($"CreateDeliveryFromSnapshot[{snap.Id}] skipped:usingCar[{snap.UsingCarId}] is not Car type");
                return null;
            }

            var d = new TransportDelivery
            {
                Id = snap.Id,
                TaskId = snap.TaskId ?? string.Empty,
                Src = snap.Src,
                Dst = snap.Dst,
                SkipFetch = snap.SkipFetch,
                SkipPut = snap.SkipPut,
                Putting = snap.Putting,
                CreateTime = snap.CreateTime,
                StartTime = snap.StartTime,
                FinishTime = snap.FinishTime,
                CarType = snap.CarType,
                UsingCar = (Car)usingCar
            };

            if (usingCar != null && string.Equals(snap.Status, "putting", StringComparison.OrdinalIgnoreCase) && !CheckCarLoaded(usingCar))
            {
                d.Error = true;
            }

            // 恢复回调配置与事件绑定
            d.ReportOnStarted = snap.ReportOnStarted;
            d.ReportOnFetched = snap.ReportOnFetched;
            d.ReportOnPut = snap.ReportOnPut;
            d.ReportOnFinished = snap.ReportOnFinished;
            d.ReportOnFailed = snap.ReportOnFailed;
            d.ReportOnTerminated = snap.ReportOnTerminated;

            d.OnStartCallbackKeys = snap.OnStartCallbackKeys ?? new List<string>();
            d.DoneFetchCallbackKeys = snap.DoneFetchCallbackKeys ?? new List<string>();
            d.DonePutCallbackKeys = snap.DonePutCallbackKeys ?? new List<string>();
            d.DoneMissionCallbackKeys = snap.DoneMissionCallbackKeys ?? new List<string>();
            d.FailedCallbackKeys = snap.FailedCallbackKeys ?? new List<string>();
            d.OnTerminatedCallbackKeys = snap.OnTerminatedCallbackKeys ?? new List<string>();

            // 统一通过 Attacher 挂载所有回调
            DeliveryCallbackAttacher.AttachAll(d);

            return d;
        }

        protected override bool CheckCarLoaded(AbstractCar car)
        {
            if(car is DummyCar)
                return true;
            var carLoaded = Commons.GetCarStatus((Car)car, "Loaded");
            if (!string.IsNullOrEmpty(carLoaded) && bool.TryParse(carLoaded, out var result))
                return result;
            return false;
        }

        /// <summary>
        /// 执行方法（启动任务调度进程）
        /// 启动基类的任务调度循环，并启动状态显示线程
        /// </summary>
        public override void Execute()
        {
            base.Execute();                 // 调用基类Execute方法，启动任务调度循环
            //HideSimpleConsole();            // 隐藏控制台窗口

            // 启动状态显示线程，实时显示小车状态和任务执行情况
            new Thread(() =>
            {
                while (true)
                {
                    Thread.Sleep(100);  // 每100ms更新一次显示
                    var painter = SimpleMonitor.getPainter("TransportMissionPainter");
                    painter.clear();
                    if (!_onDisplay) continue;  // 如果未启用显示，跳过

                    // 检查并更新任务状态回调 URL（如果配置发生变化）
                    var p = Param;
                    var currentUrl = p?.MissionCallbackURL;
                    if (!string.IsNullOrWhiteSpace(currentUrl) && currentUrl != _lastCallbackUrl)
                    {
                        // 使用 Commons 中的统一 URL 校验逻辑
                        if (Commons.IsValidHttpUrl(currentUrl))
                        {
                            TransportDeliveryCallbacks.ConfigureCallbackUrl(currentUrl);
                            _lastCallbackUrl = currentUrl;
                        }
                        else
                        {
                            Diagnosis.Log($"无效的 MissionCallbackURL 配置: {currentUrl}", "TransportMission");
                        }
                    }
                    
                    var onMissionCnt = 0;  // 正在执行任务的小车数量
                    var carStr = $"{string.Join("\n", SimpleLib.GetAllCars().Select(cc =>
                    {
                        var status = "";
                        if (cc.tags.TryGetValue("occupied", out var occupiedStr))
                        {
                            onMissionCnt++;
                            status = occupiedStr;
                            if (cc.tags.TryGetValue("idle", out var _))
                            {
                                Diagnosis.Post($"strange idle tag, {cc.name}({cc.id})");
                                cc.tags.Remove("idle");

                            }
                        }
                        else if (cc.tags.TryGetValue("idle", out var idleStr)) status = DateTime.TryParse(idleStr, out var idleTime) ? $"idle:{idleTime}" : $"idle:{idleStr}";
                        return $"{cc.name}({cc.id})\t{status}";
                    }))}";
                    //var waitingMissions = GetDeliveries()
                    //    .Where(dd => dd.GetStatus() == DeliveryStatus.Waiting).ToList();
                    painter.drawTextFixed(
                        $"小车：\t开动率：{onMissionCnt}/{SimpleLib.GetAllCars().Length}\n{carStr}\n\n",
                        new SolidBrush(Color.Black),
                        VirtualPainter.DrawPosition.LeftTop, Color.AliceBlue);
                    painter = null;
                }
            }).Start();
        }

        /// <summary>
        /// 变更任务优先级（重写基类方法）
        /// 根据配置决定是否执行就近任务策略
        /// </summary>
        public override void ChangePriority()
        {
            try
            {
                if (NearestTask == "1")   // NearestTask 等于1 执行就近原则
                {
                    // 就近任务执行（当前被注释，未启用）
                    // NearestTaskExecute();
                }
                else
                {
                    // 如果未启用就近原则，移除所有小车的就近选车标记
                    foreach (var car in SimpleLib.GetAllCars().OfType<Car>())
                    {
                        Commons.DeleteTag(car.tags, "changePriority");
                    }
                }

            }
            catch (Exception ex)
            {
                Console.WriteLine("同一工序就近执行就近任务执行：" + ex.ToString());

            }
        }

        /// <summary>
        /// 一键初始化所有小车
        /// 将状态为"正常"或"上线"且未初始化的小车进行重置
        /// </summary>
        [MethodMember(Name = "一键初始化")]
        public void ResetAll()
        {
            foreach (var car in SimpleLib.GetAllCars().OfType<Car>().Where(c => c.lstatus.Contains("正常") || c.lstatus.Contains("上线")))
            {
                // 如果小车未初始化（GetLastSite返回-1），执行重置
                if (car.GetLastSite() == -1)
                    ((Car)car).Reset();

            }
        }

        /// <summary>
        /// 一键上线所有小车
        /// 将状态为"正常"或"上线"且已初始化的小车标记为在线状态
        /// </summary>
        [MethodMember(Name = "一键上线")]
        public void OnlineAll()
        {
            foreach (var car in SimpleLib.GetAllCars().OfType<Car>().Where(c => c.lstatus.Contains("正常") || c.lstatus.Contains("上线")))
            {
                // 如果小车已初始化，标记为在线
                if (car.GetLastSite() != -1)
                {
                    Commons.AddOrUpdateTag(car.tags, "Online", "true");
                }
            }
        }

        /// <summary>
        /// 查看任务列表界面
        /// 打开任务查看器窗口，显示所有任务的状态
        /// </summary>
        [MethodMember(Name = "查看任务", Description = "显示界面")]
        public void Print()
        {
            dv = new DeliveryViewer();
            dv.Show();
        }

        /// <summary>
        /// 手动创建单取货任务
        /// 通过UI交互选择小车和取货点，创建仅取货任务（跳过取货步骤，小车从当前位置移动到取货点）
        /// </summary>
        [MethodMember(Name = "单取货", Description = "增加并排队搬运任务链")]
        public async void ManualEnqueueFetch()
        {
            try
            {
                G.pushStatus("选择小车");
                var selected = SimpleMonitor.selected.ToArray();
                if (selected.Length == 0) { CycleUiHelper.Alert("提示", "请先选择需要控制的小车！"); return; }
                var obj = selected[0];
                if (obj is Car car)
                {
                    G.pushStatus("选择取货位");
                    // 等待用户在UI中选择取货点
                    var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
                    
                    // 创建运输任务：从小车当前位置到选择的取货点
                    var d = new TransportDelivery
                    {
                        CarType = "Car",
                        Src = car.GetLastSite(),      // 起始点为小车当前位置
                        Dst = pt1.site,                // 目标点为选择的取货点
                        SkipFetch = true,              // 跳过取货步骤（因为小车已经在起始点）
                        UsingCar = car,                // 指定使用的小车
                        PutPlanInfo = new() { { "action", "fetch" } },  // 路径动作设置为fetch
                    };
                   
                    G.pushStatus($"排序了一个{car.name}搬运任务{d.Id}: {car.GetLastSite()} -> {d.Dst}");
                    Enqueue(d);
                }
                else
                {
                    CycleUiHelper.Alert("提示", "请选择需要控制的小车！");
                }
            }
            catch
            {
                G.pushStatus($"结束任务链");
            }
        }

        //写一个冒泡排序的算法，按照距离排序

        [MethodMember(Name = "切换后台显示")]
        public void SwitchBackgroundDisplay()
        {
            ShowSimpleConsole();
        }
        /// <summary>
        /// 手动创建单放货任务
        /// 通过UI交互选择小车和放货点，创建仅放货任务（小车从当前位置移动到放货点并放货）
        /// </summary>
        [MethodMember(Name = "单放货", Description = "增加并排队搬运任务链")]
        public async void ManualEnqueuePut()
        {
            try
            {
                G.pushStatus("选择小车");
                var selected = SimpleMonitor.selected.ToArray();
                if (selected.Length == 0) { CycleUiHelper.Alert("提示", "请先选择需要控制的小车！"); return; }
                var obj = selected[0];
                if (obj is Car car)
                {
                    G.pushStatus("选择取货位");
                    // 等待用户在UI中选择放货点（注释写的是"取货位"但实际是放货点）
                    var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
                    var srcSite = SimpleLib.GetSite(pt1.site);
                    
                    // 创建运输任务：从小车当前位置到选择的放货点
                    var d = new TransportDelivery
                    {
                        CarType = "Car",
                        Src = car.GetLastSite(),      // 起始点为小车当前位置
                        Dst = pt1.site,                // 目标点为选择的放货点
                        UsingCar = car,                // 指定使用的小车
                        SkipFetch = true,              // 跳过取货步骤
                        PutPlanInfo = new() { { "action", "put" } },  // 路径动作设置为put

                    };
                    G.pushStatus($"排序了一个{car.name}搬运任务{d.Id}: {srcSite.id} -> {d.Dst}");
                    Enqueue(d);
                }
                else
                {
                    CycleUiHelper.Alert("提示", "请选择需要控制的小车！");
                }
            }
            catch
            {
                G.pushStatus($"结束任务链");
            }
        }

        /// <summary>
        /// 手动创建完整的取放货任务
        /// 通过UI交互选择取货点和放货点，创建完整的搬运任务（取货->放货）
        /// </summary>
        [MethodMember(Name = "取放货", Description = "增加并排队搬运任务链")]
        public async void ManualEnqueue()
        {
            try
            {
                G.pushStatus("选择取货位");
                // 等待用户在UI中选择取货点
                var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
                var srcSite = SimpleLib.GetSite(pt1.site);
                
                G.pushStatus("选择放货位");
                // 等待用户在UI中选择放货点
                var pt2 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
                var dstSite = SimpleLib.GetSite(pt2.site);
                
                // 创建完整的运输任务：从取货点到放货点
                var d = new TransportDelivery
                {
                    CarType = "Car",
                    Src = srcSite.id,                     // 取货点
                    Dst = dstSite.id,                     // 放货点
                    TaskId = $"ManualTask-{DateTime.Now}"  // 生成任务ID
                };

                G.pushStatus($"排序了一个叉车搬运任务{d.Id}: {srcSite.id} -> {dstSite.id}");
                Enqueue(d);
            }
            catch
            {
                G.pushStatus($"结束任务链");
            }
        }

        /// <summary>
        /// 手动创建移动任务（去某地）
        /// 通过UI交互选择目标站点，创建移动任务（起点和终点相同，只移动不放货）
        /// </summary>
        [MethodMember(Name = "去某地", Description = "去某地")]
        public async void ManualEnqueueGo()
        {
            try
            {
                G.pushStatus("选择前往的站点");
                // 等待用户在UI中选择目标站点
                var pt1 = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true });
                var srcSite = SimpleLib.GetSite(pt1.site);
                
                // 创建移动任务：起点和终点相同（只移动到目标点，不执行取放货操作）
                var d = new TransportDelivery
                {
                    CarType = "Car",
                    Src = pt1.site,                     // 起点（实际不会取货）
                    Dst = pt1.site,                     // 终点（实际不会放货）
                    TaskId = $"ManualTask-{DateTime.Now}",
                    SrcFindLoop = false,                // 起点不查找回路
                    DstFindLoop = false                 // 终点不查找回路
                };

                G.pushStatus($"排序了一个叉车搬运任务{d.Id}: {srcSite.id} -> {srcSite.id}");
                Enqueue(d);
            }
            catch
            {
                G.pushStatus($"结束任务链");
            }
        }

        /// <summary>
        /// 供按钮盒进程反射调用：ButtonMission.ExecuteButtonActionInternal 反射调用本方法后，
        /// 由 HandleMethodResult 解析返回值，驱动 OnActionExecuted 成功/失败反馈。
        /// 返回 true 表示已入队；false 表示站点无效。若 Enqueue 抛错，由按钮进程捕获后同样视为失败。
        /// 按钮配置示例：TriggerMission=TransportMission，TriggerMethod=EnqueueTransportByButton，TriggerMethodParams=取货站点ID,放货站点ID
        /// </summary>
        /// <param name="srcSiteId">取货站点 ID</param>
        /// <param name="dstSiteId">放货站点 ID</param>
        public bool EnqueueTransportByButton(int srcSiteId, int dstSiteId)
        {
            if (SimpleLib.GetSite(srcSiteId) == null || SimpleLib.GetSite(dstSiteId) == null)
            {
                Diagnosis.Log($"按钮排队搬运失败：站点不存在 src={srcSiteId}, dst={dstSiteId}", "TransportMission", true);
                return false;
            }

            var d = new TransportDelivery
            {
                CarType = "Car",
                Src = srcSiteId,
                Dst = dstSiteId,
                TaskId = $"Button-{DateTime.Now:yyyyMMddHHmmssfff}"
            };
            Enqueue(d);
            Diagnosis.Post($"按钮排队搬运 {d.Id}: {srcSiteId} -> {dstSiteId}");
            return true;
        }

        // ========== 控制台窗口显示控制 ==========
        
        /// <summary>Windows API：获取控制台窗口句柄</summary>
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        /// <summary>Windows API：显示/隐藏窗口</summary>
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        /// <summary>控制台显示状态标志</summary>
        private static bool ShowConsole = false;

        /// <summary>
        /// 切换控制台窗口显示/隐藏
        /// </summary>
        public static void ShowSimpleConsole()
        {
            ShowConsole = !ShowConsole;  // 切换显示状态
            var handle = GetConsoleWindow();
            int n = ShowConsole ? 0 : 5;  // 0=显示，5=隐藏
            Console.WriteLine(n);
            ShowWindow(handle, n);
        }

        /// <summary>
        /// 隐藏控制台窗口
        /// </summary>
        public static void HideSimpleConsole()
        {
            var handle = GetConsoleWindow();
            ShowWindow(handle, 0);  // 0表示隐藏窗口
        }
    }

}
