using ServiceCenter.Extensions;
using ServiceCenter.Logs;
using ServiceCenter.SqlSugars;
using SqlSugar;
using System.ComponentModel;
using WCS.Core;
using WCS.Entity;
using WCS.Entity.Protocol.Protocol.Robot;
using WCS.Entity.Protocol.Robot;
using WCS.Entity.Protocol.Station;
using WCS.WorkEngineering.Extensions;
using WCS.WorkEngineering.WebApi.Controllers;
using WCS.WorkEngineering.Worlds;
using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
using TaskStatus = WCS.Entity.TaskStatus;

namespace WCS.WorkEngineering.Systems
{
    /// <summary>
    ///   机械臂
    /// </summary>
    [BelongTo(typeof(RingWorld))]
    [Description("机械臂")]
    public class 机械臂 : DeviceSystem<Device<IRobot520, IRobot521, IRobot522>>
    {
        protected override bool ParallelDo => true;

        protected override bool SaveLogsToFile => true;

        /// <summary>
        /// 取货点设备集合
        /// </summary>
        private Dictionary<string, List<Device>> PickUpDevices = new();

        /// <summary>
        /// 放货设备
        /// </summary>
        private Dictionary<string, List<Device<IStation520, IStation521, IStation523>>> PutDevices = new();

        public 机械臂()
        {
            //获取所有的机械臂 集合
            var devices = Device.All.Where(v => v.HasFlag(DeviceFlags.Robot));

            //开始分配
            foreach (var robot in devices)
            {
                //取货设备
                PickUpDevices.Add(robot.Code, robot.Sources.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => v).ToList());
                PutDevices.Add(robot.Code, robot.Targets.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => new Device<IStation520, IStation521, IStation523>(v, World)).ToList());
            }
        }

        public override void Do(Device<IRobot520, IRobot521, IRobot522> obj)
        {
            #region 处理完成任务

            //判断DB520 完成任务确认清除信号 是否为1
            if (obj.Data.OkAck == 1 && obj.Data2.TaskFinishId1 == 0 || obj.Data2.TaskFinishId2 == 0) obj.Data.OkAck = 0;

            if (obj.Data2.TaskFinishId1 > 0 || obj.Data2.TaskFinishId2 > 0)
            {
                //处理完成的任务信息
                var tasks = new List<WCS_TaskInfo>();
                //开始处理
                SqlSugarHelper.Do(db =>
                {
                    World.Log($"机械臂任务处理:开始--完成任务{obj.Data2.TaskFinishId1}--{obj.Data2.TaskFinishId2}", LogLevelEnum.Low);

                    //根据DB521任务号获取对应任务
                    var taskInfoList = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.ID == obj.Data2.TaskFinishId1 || v.ID == obj.Data2.TaskFinishId2).ToList();
                    foreach (var task in taskInfoList.Where(task => task.Status == Entity.TaskStatus.StackerExecution))
                    {
                        //根据任务类型做不同的处理
                        switch (task.Type)
                        {
                            case TaskType.SetPlate: //工字轮入库
                                //完成任务
                                task.Status = Entity.TaskStatus.Finish;
                                task.EndTime = DateTime.Now;
                                db.Default.Updateable(task).ExecuteCommand();
                                task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "入库任务结束");
                                break;

                            case TaskType.OutDepot:
                                task.Status = Entity.TaskStatus.ConveyorExecution;
                                task.EndTime = DateTime.Now;
                                db.Default.Updateable(task).ExecuteCommand();
                                task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "出库任务结束");

                                break;

                            case TaskType.TransferDepot:
                                task.Status = Entity.TaskStatus.Finish;
                                task.EndTime = DateTime.Now;
                                db.Default.Updateable(task).ExecuteCommand();
                                task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "移库任务结束");
                                break;
                        }
                        tasks.Add(task);
                    }
                });
                if (!tasks.Any()) throw new KnownException("数据库提交事务错误", LogLevelEnum.High);
                foreach (var task in tasks)
                {
                    //通知WMS任务完成
                    if (task.Status == TaskStatus.Finish) WmsApi.CompleteTask(task.ID);
                    //如果是出库任务，上报放货完成
                    if (task is { Type: TaskType.OutDepot, Status: TaskStatus.ConveyorExecution }) WmsApi.SrmPickOutCompleted(task.ID);
                }

                // 写入信号
                obj.Data.OkAck = 1;

                World.Log($"机械臂任务处理:结束--完成任务{obj.Data2.TaskFinishId1}--{obj.Data2.TaskFinishId2}", LogLevelEnum.Mid);
            }

            #endregion 处理完成任务

            //robot是否可以下发任务
            if (obj.Data2.VoucherNo != obj.Data.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{obj.Data.VoucherNo},DB521:{obj.Data2.VoucherNo}", LogLevelEnum.Mid);
            if (obj.Data2.RobotMode != RobotMode.Automatic) throw new KnownException($"robot处于{obj.Data2.RobotMode.GetDescription()}模式", LogLevelEnum.Low);
            if (obj.Data2.RunStatus != RobotRunStatus.Idle) throw new KnownException($"robot处于{obj.Data2.RunStatus.GetDescription()}状态", LogLevelEnum.High);

            //上一个周期是不是出库任务 第一次获取返回结果会是false
            var lastIsOut = obj.Entity.GetFlag<bool>("LastIsOut");
            obj.Entity.SetFlag("LastIsOut", !lastIsOut);

            //判断是否有任务是机器人执行中
            //再检查是否有等待执行的货物
            SqlSugarHelper.Do(db =>
            {
                //获取当前堆垛机的所有未完成任务
                var tasks = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Status < Entity.TaskStatus.Finish && v.Device == obj.Entity.Code);
                //任务集合是否有处于堆垛机执行状态的任务
                if (tasks.Any(v => v.Status == Entity.TaskStatus.StackerExecution)) throw new KnownException($"有任务处于堆垛机执行状态", LogLevelEnum.High);
            });

            //入库任务优先 或 上一个周期是出库任务并且出库任务无优先
            if (lastIsOut) //入库任务
            {
                //判断本次优先执行楼层,并设置下次执行时优先楼层
                var floor = obj.Entity.GetFlag<int>("FloorIn");
                floor = floor % 2 + 1;
                obj.Entity.SetFlag("FloorIn", floor);

                //获取当前机械臂所有的取货站台
                var pickUpDevices = obj.Entity.Sources.Where(x => x.HasFlag(DeviceFlags.输送机)).Where(x => x.DeviceGroup.Any()).Select(
                    x =>
                    {
                        var group = x.DeviceGroup.Select(s => new Device<IStation523, IStation524>(s, World)).ToList();
                        return new Tuple<Device<IStation523, IStation524>, List<Device<IStation523, IStation524>>>(new Device<IStation523, IStation524>(x, World), group);
                    }).ToList();

                if (!pickUpDevices.Any()) throw new KnownException($"机械臂{obj.Entity.Code}无取货路径点", LogLevelEnum.High);

                //获取取货设备的设备组中都是停止运行的
                var arrIn = pickUpDevices.Where(x => x.Item2.All(a => !a.Data.Status.HasFlag(StationStatus.Run)))
                                                      .Where(x => x.Item2.OrderBy(o => o.Entity.Code.ToShort()) //前两个设备中有取货任务的
                                                                                               .Take(2)
                                                                                               .Any(a => a.Data2.TaskNumber > 0 && a.Data.Status.HasFlag(StationStatus.PH_Status)))
                                                      .ToList();

                if (!arrIn.Any()) throw new KnownException($"[{obj.Entity.Code}]等待入库任务输送到位", LogLevelEnum.Mid);
                //等待下发的任务信息
                var taskList = new List<Tuple<WCS_TaskInfo, Device<IStation523, IStation524>>>();
                SqlSugarHelper.Do(db =>
                {
                    //跟据设备组中第一个设备任务号最小的一个先执行
                    var devGroup = arrIn.MinBy(x => x.Item2.OrderBy(o => o.Data2.TaskNumber).FirstOrDefault().Data2.TaskNumber);
                    foreach (var dev in devGroup.Item2.OrderByDescending(x => x.Entity.Code.ToShort()).Take(2))
                    {
                        var task = db.Default.Queryable<WCS_TaskInfo>().First(v => v.Type == TaskType.SetPlate && v.Status == Entity.TaskStatus.FinishOfShunt && dev.Data2.TaskNumber == v.ID);
                        if (task == null) continue;
                        var res = WmsApi.RingApplyStockInLoc(task.ID, task.Device, dev.Entity.Code, task.GoodsType);
                        task.Status = Entity.TaskStatus.StackerExecution;
                        task.AddrTo = res.ResData.CellNo;
                        task.Line = res.ResData.Row;
                        task.Col = res.ResData.Colomn;
                        task.Layer = res.ResData.Layer;
                        task.Depth = res.ResData.Row;
                        task.LastInteractionPoint = dev.Entity.Code;
                        task.SrmStation = dev.Entity.Code;
                        task.Device = obj.Entity.Code;
                        task.EditWho = "WCS";
                        db.Default.Updateable(task).ExecuteCommand();
                        task.AddWCS_TASK_DTL(db.Default, dev.Entity.Code, task.AddrTo, "任务下发机械臂执行");
                        taskList.Add(new(task, dev));
                    }
                });

                if (!taskList.Any()) return;

                switch (taskList.Count)
                {
                    case 1:
                        var task = taskList.FirstOrDefault().Item1;
                        var dev = taskList.FirstOrDefault().Item2;
                        //下发任务
                        obj.Data.TaskNumber1 = task.ID;
                        obj.Data.SLine1 = dev.Entity.Code.ToShort();
                        obj.Data.SCol1 = 0;
                        obj.Data.SLayer1 = 0;
                        obj.Data.SDepth1 = 0;
                        obj.Data.ELine1 = task.Line.ToShort();
                        obj.Data.ECol1 = task.Col.ToShort();
                        obj.Data.ELayer1 = task.Layer.ToShort();
                        obj.Data.EDepth1 = task.Depth.ToShort();
                        obj.Data.TaskNumber2 = 0;
                        obj.Data.SLine2 = 0;
                        obj.Data.SCol2 = 0;
                        obj.Data.SLayer2 = 0;
                        obj.Data.SDepth2 = 0;
                        obj.Data.ELine2 = 0;
                        obj.Data.ECol2 = 0;
                        obj.Data.ELayer2 = 0;
                        obj.Data.EDepth2 = 0;
                        obj.Data.TaskSum = taskList.Count.ToShort();
                        obj.Data.GoodsType = task.GoodsType switch
                        {
                            18 => 1,
                            34 => 2,
                            50 => 3,
                            _ => 0
                        };
                        obj.Data.TaskType = 3;
                        obj.Data.VoucherNo++;
                        break;

                    case 2:
                        taskList = taskList.OrderBy(x => x.Item1.Depth).ToList();
                        //一工位取深度较大的任务
                        var taskInfo = taskList[1];
                        task = taskInfo.Item1;
                        dev = taskInfo.Item2;
                        obj.Data.TaskNumber1 = task.ID;
                        obj.Data.SLine1 = dev.Entity.Code.ToShort();
                        obj.Data.SCol1 = 0;
                        obj.Data.SLayer1 = 0;
                        obj.Data.SDepth1 = 0;
                        obj.Data.ELine1 = task.Line.ToShort();
                        obj.Data.ECol1 = task.Col.ToShort();
                        obj.Data.ELayer1 = task.Layer.ToShort();
                        obj.Data.EDepth1 = task.Depth.ToShort();
                        //二工位取深度较少的值
                        taskInfo = taskList[0];
                        task = taskInfo.Item1;
                        dev = taskInfo.Item2;
                        obj.Data.TaskNumber2 = task.ID;
                        obj.Data.SLine2 = dev.Entity.Code.ToShort();
                        obj.Data.SCol2 = 0;
                        obj.Data.SLayer2 = 0;
                        obj.Data.SDepth2 = 0;
                        obj.Data.ELine2 = task.Line.ToShort();
                        obj.Data.ECol2 = task.Col.ToShort();
                        obj.Data.ELayer2 = task.Layer.ToShort();
                        obj.Data.EDepth2 = task.Depth.ToShort();
                        obj.Data.TaskSum = taskList.Count.ToShort();
                        obj.Data.GoodsType = task.GoodsType switch
                        {
                            18 => 1,
                            34 => 2,
                            50 => 3,
                            _ => 0
                        };
                        obj.Data.TaskType = 3;
                        obj.Data.VoucherNo++;
                        break;

                    default:
                        throw new KnownException($"无法执行多个任务", LogLevelEnum.Mid);
                }
            }
            else if (!lastIsOut) //出库任务
            {
                //判断本次优先执行楼层,并设置下次执行时优先楼层
                var floor = obj.Entity.GetFlag<int>("FloorOut");
                floor = floor % 2 + 1;
                obj.Entity.SetFlag("FloorOut", floor);

                //获取当前堆垛机所有的取货站台
                var arrOut = PutDevices.First(v => v.Key == obj.Entity.Code).Value;
                if (!arrOut.Any()) throw new KnownException($"机械臂{obj.Entity.Code}无放货路径点", LogLevelEnum.High);

                //获取可以放货的设备集合
                arrOut = arrOut.Where(v => v.Data3.Status.HasFlag(StationStatus.PH_Status)).ToList();//无光电
                if (!arrOut.Any()) throw new KnownException($"[{obj.Entity.Code}]等待出库任务输送到位", LogLevelEnum.Mid);

                var taskInfoList = new List<WCS_TaskInfo>();
                var nextAdd = "";

                SqlSugarHelper.Do(db =>
                        {
                            //所有出库点设备号，即
                            var allOutCode = arrOut.Select(v => v.Entity.Code).ToList();
                            //TODO:两个工位同时码垛，并不会一个执行一次，只有一个托盘任务全部执行完毕，才会继续执行下一个，设备号较小的码垛工位会优先执行
                            var taskInfos = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Type == TaskType.OutDepot && v.Status == Entity.TaskStatus.WaitingToExecute)
                                                                                                    .Where(v => allOutCode.Contains(v.SrmStation)).ToList()
                                                                                                    .OrderBy(x => x.SrmStation.ToInt()) //按照码垛站台号进行一次排序
                                                                                                    .ThenBy(x => x.ProdLine) //码垛从小到大
                                                                                                    .ThenBy(x => x.AddTime)//任务生成时间从早到晚
                                                                                                    .Take(2).ToList();
                            if (taskInfos.Count == 2) //有两个任务
                            {
                                var minDepth = taskInfos!.MinBy(x => x.Depth);
                                var maxDepth = taskInfos.MaxBy(x => x.Depth);
                                //物料号不同时只执行一个任务 不同的轮子物料号也不同
                                if (minDepth.MatCode != maxDepth.MatCode) taskInfos = taskInfos.OrderBy(x => x.ProdLine).Take(1).ToList();
                                //深度之和等于6(机械臂当前无法同时执行两个三深度的取货任务)
                                else if (taskInfos.Sum(x => x.Depth) == 6) taskInfos = taskInfos.OrderBy(x => x.ProdLine).Take(1).ToList();
                            }

                            foreach (var task in taskInfos)
                            {
                                task.Status = Entity.TaskStatus.StackerExecution;
                                task.LastInteractionPoint = task.Device;
                                task.EditWho = "WCS";
                                nextAdd = task.SrmStation;
                                db.Default.Updateable(task).ExecuteCommand();
                                task.AddWCS_TASK_DTL(db.Default, task.Device, task.SrmStation, "任务下发机械臂执行");
                                taskInfoList.Add(task);
                            }
                        });

                if (!taskInfoList.Any()) return;

                switch (taskInfoList.Count)
                {
                    case 1:
                        var task = taskInfoList.FirstOrDefault();

                        //下发任务
                        obj.Data.TaskNumber1 = task.ID;
                        obj.Data.SLine1 = task.Line.ToShort();
                        obj.Data.SCol1 = task.Col.ToShort();
                        obj.Data.SLayer1 = task.Layer.ToShort();
                        obj.Data.SDepth1 = task.Depth.ToShort();
                        obj.Data.ELine1 = nextAdd.ToShort();
                        obj.Data.ECol1 = task.ProdLine;
                        obj.Data.ELayer1 = 0;
                        obj.Data.EDepth1 = 0;
                        obj.Data.TaskNumber2 = 0;
                        obj.Data.SLine2 = 0;
                        obj.Data.SCol2 = 0;
                        obj.Data.SLayer2 = 0;
                        obj.Data.SDepth2 = 0;
                        obj.Data.ELine2 = 0;
                        obj.Data.ECol2 = 0;
                        obj.Data.ELayer2 = 0;
                        obj.Data.EDepth2 = 0;
                        obj.Data.TaskSum = taskInfoList.Count.ToShort();
                        obj.Data.GoodsType = task.GoodsType switch
                        {
                            18 => 1,
                            34 => 2,
                            50 => 3,
                            _ => 0
                        };
                        obj.Data.TaskType = 4;
                        obj.Data.VoucherNo++;
                        break;

                    case 2:

                        taskInfoList = taskInfoList.OrderBy(x => x.Depth).ToList();

                        //一工位取深度较大的任务
                        var taskInfo = taskInfoList[1];
                        obj.Data.TaskNumber1 = taskInfo.ID;
                        obj.Data.SLine1 = taskInfo.Line.ToShort();
                        obj.Data.SCol1 = taskInfo.Col.ToShort();
                        obj.Data.SLayer1 = taskInfo.Layer.ToShort();
                        obj.Data.SDepth1 = taskInfo.Depth.ToShort();
                        obj.Data.ELine1 = nextAdd.ToShort();
                        obj.Data.ECol1 = taskInfoList.Min(x => x.ProdLine);
                        obj.Data.ELayer1 = 0;
                        obj.Data.EDepth1 = 0;
                        //二工位取深度较少的值
                        taskInfo = taskInfoList[0];
                        obj.Data.TaskNumber2 = taskInfo.ID;
                        obj.Data.SLine2 = taskInfo.Line.ToShort();
                        obj.Data.SCol2 = taskInfo.Col.ToShort();
                        obj.Data.SLayer2 = taskInfo.Layer.ToShort();
                        obj.Data.SDepth2 = taskInfo.Depth.ToShort();
                        obj.Data.ELine2 = nextAdd.ToShort();
                        obj.Data.ECol2 = taskInfoList.Max(x => x.ProdLine);
                        obj.Data.ELayer2 = 0;
                        obj.Data.EDepth2 = 0;
                        obj.Data.TaskSum = taskInfoList.Count.ToShort();
                        obj.Data.GoodsType = taskInfo.GoodsType switch
                        {
                            18 => 1,
                            34 => 2,
                            50 => 3,
                            _ => 0
                        };
                        obj.Data.TaskType = 4;
                        obj.Data.VoucherNo++;
                        break;

                    default:

                        throw new KnownException($"无法执行多个任务", LogLevelEnum.Mid);
                }
            }
        }

        public override bool Select(Device dev)
        {
            return dev.HasFlag(DeviceFlags.Robot);
        }
    }
}
