using PlcSiemens.Core.Extension;
using ServiceCenter.Extensions;
using ServiceCenter.SqlSugars;
using System.ComponentModel;
using WCS.Core;
using WCS.Entity;
using WCS.Entity.Protocol.SRM;
using WCS.Entity.Protocol.Station;
using WCS.WorkEngineering.Extensions;
using WCS.WorkEngineering.WebApi.Controllers;
using WCS.WorkEngineering.Worlds;
using WCS.WorkEngineering.Worlds.Logs;
using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
using KnownException = WCS.WorkEngineering.Worlds.Logs.KnownException;
using TaskStatus = WCS.Entity.TaskStatus;

namespace WCS.WorkEngineering.Systems
{
    /// <summary>
    /// 堆垛机系统
    /// </summary>
    [BelongTo(typeof(MainWorld))]
    [Description("堆垛机系统")]
    public class SrmSystems : DeviceSystem<SRM>
    {
        /// <summary>
        /// 取货点设备集合
        /// </summary>
        private Dictionary<string, List<Station>> PickUpDevices = new Dictionary<string, List<Station>>();

        /// <summary>
        /// 放货设备
        /// </summary>
        private Dictionary<string, List<Station>> PutDevices = new Dictionary<string, List<Station>>();

        public SrmSystems()
        {
            //获取所有的巷道集合
            var devices = Device.All.Where(v => v.HasFlag(DeviceFlags.巷道));

            //开始分配
            foreach (var item in devices)
            {
                //取货设备
                var srm = item.Targets.Where(v => v.HasFlag(DeviceFlags.堆垛机)).FirstOrDefault();
                PickUpDevices.Add(srm.Code, item.Targets.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => new Station(v, this.World)).ToList());
                //放货设备
                srm = item.Sources.Where(v => v.HasFlag(DeviceFlags.堆垛机)).FirstOrDefault();
                PutDevices.Add(srm.Code, item.Sources.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => new Station(v, this.World)).ToList());
            }
        }

        protected override bool ParallelDo => true;

        protected override bool SaveLogsToFile => true;

        public override void Do(SRM obj)
        {
            //判断堆垛机是否报警
            if (obj.Data2.Status.HasFlag(SrmStatus.Alarm)) throw new KnownException(obj.Data3.Alarm.ToString(), LogLevelEnum.High);

            //判断DB520 完成任务确认清除信号 是否为1

            if (obj.Data.OkAck == 1) throw new KnownException("任务完成确认信号未清除", LogLevelEnum.Mid);

            //判断完成任务号是否大于0
            if (obj.Data2.TaskFinishiId > 0)
            {
                WCS_TaskInfo taskInfo = null;
                SqlSugarHelper.Do(db =>
                  {
                      //根据DB521任务号获取对应任务
                      var task = db.Default.Queryable<WCS_TaskInfo>().First(v => v.ID == obj.Data2.TaskFinishiId) ?? throw new KnownException($"未找到任务{obj.Data2.TaskFinishiId}", LogLevelEnum.High);
                      if (task.Status != Entity.TaskStatus.StackerExecution)
                          throw new KnownException($"任务{task.ID}状态是{task.Status.Description()}.堆垛机完成任务需要对应任务状态处于堆垛机执行中", LogLevelEnum.High);
                      //根据任务类型做不同的处理
                      switch (task.Type)
                      {
                          case TaskType.EnterDepot:
                              //完成任务
                              task.Status = Entity.TaskStatus.Finish;
                              task.EedTime = DateTime.Now;
                              task.AddWCS_TASK_DTL(db, task.AddrTo, "入库任务结束");
                              break;

                          case TaskType.OutDepot:
                              switch (task.OutType)
                              {
                                  case OutTypeEnum.自动出库任务:
                                      task.Status = Entity.TaskStatus.ConveyorExecution;
                                      var dev = new Station(Device.All.FirstOrDefault(v => v.Code == task.SrmStation), this.World);
                                      dev.Data.TaskNumber = task.ID;
                                      task.AddWCS_TASK_DTL(db, task.SrmStation, "出库任务到达放货站台");
                                      break;

                                  case OutTypeEnum.全自动手动出库任务:

                                      break;

                                  case OutTypeEnum.半自动手动出库任务:
                                      task.Status = Entity.TaskStatus.Finish;
                                      task.AddWCS_TASK_DTL(db, task.SrmStation, "半自动手动出库任务结束");
                                      break;

                                  default:
                                      break;
                              }

                              break;

                          case TaskType.TransferDepot:
                              task.Status = Entity.TaskStatus.Finish;
                              task.EedTime = DateTime.Now;
                              task.AddWCS_TASK_DTL(db, task.AddrTo, "移库任务结束");
                              break;

                          case TaskType.EmptyInit:
                              task.Status = Entity.TaskStatus.Finish;
                              task.EedTime = DateTime.Now;
                              task.AddWCS_TASK_DTL(db, task.AddrTo, "移库任务结束");
                              break;
                      }

                      db.Default.Updateable(task).ExecuteCommand();
                      if (task.Status >= TaskStatus.Finish) task.CompleteOrCancelTasks(db);

                      taskInfo = task;
                  });
                if (taskInfo == null) throw new KnownException("数据库提交事务错误", LogLevelEnum.High);
                //通知WMS任务完成
                if (taskInfo.Status == TaskStatus.Finish) WmsApi.CompleteTask(taskInfo.ID);
                if (taskInfo.Type == TaskType.OutDepot && taskInfo.Status == TaskStatus.ConveyorExecution) WmsApi.SrmPickOutCompleted(taskInfo.ID);
                // 写入信号
                obj.Data.OkAck = 1;
            }

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

            //默认没有移库任务
            bool isTransfer = false;

            //出入库优先级任务 1:无优先 2:入库 3:出库
            int enterOrOut = 1;

            //再检查是否有等待执行的货物
            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 (!tasks.Any(v => v.Type != TaskType.TransferDepot && v.Status < Entity.TaskStatus.StackerExecution && v.Priority > 0))
                {
                    //不存在调整优先级任务,判断是否存在移库任务
                    isTransfer = tasks.Any(v => v.Type == TaskType.TransferDepot && v.Status == Entity.TaskStatus.NewBuild);
                }
                else //存在调整优先级任务
                {
                    //获取出库任务中新建状态最大优先级
                    var outPriorityNewBuild = tasks.Where(v => v.Type == TaskType.OutDepot && v.Status == Entity.TaskStatus.NewBuild).Max(v => v.Priority);
                    //获取入库任务中最大优先级
                    var enterPriority = tasks.Where(v => v.Type == TaskType.EnterDepot && v.Status < Entity.TaskStatus.StackerExecution).Max(v => v.Priority);
                    //出入库最大优先级相加大于零
                    if (outPriorityNewBuild + enterPriority > 0)
                    {
                        //出入库优先级任务 1:无优先 2:入库 3:出库
                        enterOrOut = enterPriority > outPriorityNewBuild ? 2 : 3;
                    }
                }
            });

            #region 移库

            if (isTransfer)
            {
                WCS_TaskInfo taskInfo = null;
                SqlSugarHelper.Do(db =>
                   {
                       //获取一条当前堆垛机优先级最高的新建移库任务
                       var task = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Device == obj.Entity.Code && v.Type == TaskType.TransferDepot && v.Status == Entity.TaskStatus.NewBuild)
                                                                      .OrderByDescending(v => v.Priority)
                                                                      .First() ?? throw new KnownException("未找到移库任务", LogLevelEnum.High);
                       //任务状态改为堆垛机执行中
                       task.Status = Entity.TaskStatus.StackerExecution;
                       task.StartTime = DateTime.Now;
                       task.AddWCS_TASK_DTL(db, task.AddrFrom, task.Device, $"堆垛机{obj.Entity.Code}开始执行任务");
                       db.Default.Updateable(task).AddQueue();
                       db.Default.SaveQueues();
                       taskInfo = task;
                   });
                if (taskInfo == null) throw new KnownException("数据更新错误", LogLevelEnum.High);
                var addrFrom = taskInfo.AddrFrom.Split("-");
                var addrTo = taskInfo.AddrTo.Split("-");
                //下发任务
                obj.Data.TaskNumber = taskInfo.ID;
                obj.Data.SLine = addrFrom[0].ToShort();
                obj.Data.SCol = addrFrom[1].ToShort();
                obj.Data.SLayer = addrFrom[2].ToShort();
                obj.Data.ELine = addrTo[0].ToShort();
                obj.Data.ECol = addrTo[1].ToShort();
                obj.Data.ELayer = addrTo[2].ToShort();
                obj.Data.TaskType = SrmTaskTypeEnum.MoveGoods;
                obj.Data.VoucherNo++;
            }

            #endregion 移库

            #region 出入库

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

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

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

                //获取有货的设备
                arrIn = arrIn.Where(v => v.Data.TaskNumber > 0 && v.Data3.Status.HasFlag(StatusEunm.PH_Status) && !v.Data3.Status.HasFlag(StatusEunm.Run)).ToList();
                if (!arrIn.Any()) throw new KnownException($"[{obj.Entity.Code}]等待入库任务输送到位", LogLevelEnum.Mid);
                WCS_TaskInfo taskInfo = null;
                Station station = null;
                SqlSugarHelper.Do(db =>
                {
                    //根据有货设备的任务号获取所有类型为入库状态为输送机执行中的任务
                    var tasks = db.Default.Queryable<WCS_TaskInfo>().Where(v => (v.Type == TaskType.EnterDepot || v.Type == TaskType.EmptyInit)
                                                                              && v.Status == TaskStatus.ConveyorExecution
                                                                              && arrIn.Select(p => p.Data.TaskNumber).Contains(v.ID));
                    //按条件先后排序获取一条排序后第一条结果1.优先级2.所在楼层与本次优先执行楼层 TODO:待验证排序结果
                    var task = tasks.OrderByDescending(v => v.Priority).OrderByDescending(v => v.Floor == floor ? 1 : 0).First() ?? throw new KnownException($"{obj.Entity.Code}未找到入库任务", LogLevelEnum.High);
                    //获取任务所有设备
                    station = arrIn.First(v => v.Data.TaskNumber == task.ID);
                    //获取当前货物巷道
                    var res = WmsApi.GetLocalIn(task.ID, task.Device, station.Entity.Code);
                    var loc = res.ResData.CellNo.Split("-");
                    task.Status = TaskStatus.StackerExecution;
                    task.AddrTo = $"{loc[0]}-{loc[1]}-{loc[2]}";
                    task.LastInteractionPoint = station.Entity.Code;
                    task.EditWho = "WCS";
                    task.AddWCS_TASK_DTL(db, station.Entity.Code, task.AddrTo, "任务下发堆垛机执行");
                    db.Default.Updateable(task).ExecuteCommand();
                    taskInfo = task;
                });

                if (taskInfo == null) throw new KnownException("数据更新错误", LogLevelEnum.High);
                var addrTo = taskInfo.AddrTo.Split("-");
                //下发任务
                obj.Data.TaskNumber = taskInfo.ID;
                obj.Data.TaskType = SrmTaskTypeEnum.Default;
                obj.Data.SLine = station.Entity.Code.ToShort();
                obj.Data.SCol = 0;
                obj.Data.SLayer = 0;
                obj.Data.ELine = addrTo[0].ToShort();
                obj.Data.ECol = addrTo[1].ToShort();
                obj.Data.ELayer = addrTo[2].ToShort();
                obj.Data.VoucherNo++;
            }
            else if (enterOrOut == 3 || !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(StatusEunm.PH_Status) //无光电
                                                && !v.Data3.Status.HasFlag(StatusEunm.Run) //未运行
                                                && !v.Data3.Status.HasFlag(StatusEunm.OT_Status) //无任务
                                                && !v.Data3.Status.HasFlag(StatusEunm.UnassignedTask) //未分配任务
                                                && v.Data3.Status.HasFlag(StatusEunm.Auto)).ToList(); //自动

                if (!arrOut.Any()) throw new KnownException($"[{obj.Entity.Code}]等待出库任务输送到位", LogLevelEnum.Mid);

                WCS_TaskInfo taskInfo = null;

                SqlSugarHelper.Do(db =>
               {
                   var allOutCode = arrOut.Select(v => v.Entity.Code).ToList();

                   var task = taskInfo.GetOutTask(db, allOutCode, floor, obj);

                   task.Status = TaskStatus.StackerExecution;
                   task.LastInteractionPoint = task.Device;
                   task.EditWho = "WCS";
                   task.AddWCS_TASK_DTL(db, task.Device, task.SrmStation, "任务下发堆垛机执行");
                   db.Default.Updateable(task).AddQueue();
                   taskInfo = task;
                   db.Default.SaveQueues();
               });

                if (taskInfo == null) throw new KnownException("数据更新错误", LogLevelEnum.High);

                var addrFrom = taskInfo.AddrFrom.Split("-");

                obj.Data.TaskNumber = taskInfo.ID;
                obj.Data.SLine = addrFrom[0].ToShort();
                obj.Data.SCol = addrFrom[1].ToShort();
                obj.Data.SLayer = addrFrom[2].ToShort();
                obj.Data.ELine = taskInfo.SrmStation.ToShort();
                obj.Data.ECol = 0;
                obj.Data.ELayer = 0;
                obj.Data.TaskType = SrmTaskTypeEnum.Default;
                obj.Data.VoucherNo++;
            }

            #endregion 出入库
        }

        public override bool Select(Device dev)
        {
            //return dev.Code == "SRM3";
            return dev.HasProtocol(typeof(ISRM520));
        }
    }
}
