using Newtonsoft.Json;
using ServiceCenter.Extensions;
using ServiceCenter.Logs;
using ServiceCenter.Redis;
using ServiceCenter.SqlSugars;
using SqlSugar;
using System.ComponentModel;
using WCS.Core;
using WCS.Entity;
using WCS.Entity.Protocol.Station;
using WCS.Entity.Protocol.Truss;
using WCS.WorkEngineering.Extensions;
using WCS.WorkEngineering.Worlds;
using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
using TaskStatus = WCS.Entity.TaskStatus;
using WCS_CacheLine = WCS.Entity.WCS_CacheLine;
using WCS_CacheLineLoc = WCS.Entity.WCS_CacheLineLoc;

namespace WCS.WorkEngineering.Systems.分拣支线
{
    /// <summary>
    ///  桁架分流点
    /// </summary>
    [BelongTo(typeof(SortingBranchWorld))]
    [Description("桁架分流点")]
    public class 桁架分流点 : DeviceSystem<Device<IStation520, IStation521, IStation523>>
    {
        protected override bool ParallelDo => true;

        public override void Do(Device<IStation520, IStation521, IStation523> obj)
        {
            if (obj.Data.VoucherNo != obj.Data2.VoucherNo)
            {
                World.Log($"凭证号不一致,DB520:{obj.Data.VoucherNo}-DB521:{obj.Data2.VoucherNo}");
                return;
            }

            if (obj.Data3.Status.HasFlag(StationStatus.Run))
            {
                World.Log("设备运行中");
                return;
            }

            if (!obj.Data3.Status.HasFlag(StationStatus.OT_Status))
            {
                World.Log("站台货物信息与实际占用不一致");
                return;
            }

            ;
            if (obj.Data2.Request != 1)
            {
                World.Log("无请求");
                return;
            }

            WCS_TaskInfo taskInfo = null;
            SqlSugarHelper.Do(_db =>
            {
                var db = _db.Default;
                var task =
                    db.Queryable<WCS_TaskInfo>()
                        .First(v => v.ID == obj.Data2.TaskNumber && v.Status < TaskStatus.FinishOfShunt) ??
                    throw new KnownException($"未找到对应的WCS任务{obj.Data2.TaskNumber}", LogLevelEnum.Mid);
                if (Allot(db, task, obj)) taskInfo = task;
            });

            if (taskInfo == null) return;

            obj.Data.TaskNumber = obj.Data2.TaskNumber;
            obj.Data.TaskNumber = obj.Data2.TaskNumber;
            obj.Data.GoodsStart = obj.Entity.Code.ToShort();
            obj.Data.GoodsStart = obj.Entity.Code.ToShort();
            obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
            obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
            obj.Data.SetVoucherNo();
            World.Log($"执行记录:任务号[{obj.Data2.TaskNumber}][{obj.Data.VoucherNo}]");
        }

        public override bool Select(Device dev)
        {
            return dev.HasFlag(Extensions.DeviceFlags.桁架分流点);
        }

        /// <summary>
        ///  计算去向
        /// </summary>
        /// <param name="db"></param>
        /// <param name="taskInfo"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool Allot(SqlSugarScopeProvider db, WCS_TaskInfo taskInfo,
            Device<IStation520, IStation521, IStation523> obj)
        {
            var nowTime = DateTime.Now;
            taskInfo.Device = obj.Entity.Code switch
            {
                "455" => "Truss1",
                "655" => "Truss1",
                "855" => "Truss2",
                "1055" => "Truss2",
                "1255" => "Truss3",
                "1455" => "Truss3",
                _ => "Error"
            };
            //获取当前货物类型可以到达的桁架缓存放行点信息
            var cacheLineDevList = obj.Entity.Targets.Where(x => x.HasFlag(DeviceFlags.桁架缓存放行点)); //当前地址可以到达的所有桁架缓存放行点信息

            switch (taskInfo.GoodsType)
            {
                case 9:
                    //是异常工字轮
                    cacheLineDevList = taskInfo.BarCode.Contains("Error")
                        ? cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09异常缓存放行点))
                        : cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09缓存放行点));
                    break;

                case 18:
                    cacheLineDevList = cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架18缓存放行点));
                    break;
            }
            var value = RedisHub.Default.Get("TrussTunnelForbidden");
            if (!value.Contains("["))
            {
                World.Log($"没有桁架巷道禁用配置信息，请检查", LogLevelEnum.High);
                return false;
            }
            var trussTunnelForbidden = JsonConvert.DeserializeObject<List<string>>(value);
            //排除禁用位置
            cacheLineDevList = cacheLineDevList.Where(x => !trussTunnelForbidden.Contains(x.Code));
            var cacheLineCodes = cacheLineDevList.Select(x => x.Code.ToShort());
            var cacheLineList = db.Queryable<WCS_CacheLine>().Includes(x => x.Locations).ToList();

            #region 跟据缓存信息寻找可以到达的缓存点

            //找到当前任务可用的缓存线信息
            var cacheLine = cacheLineList.Where(x => x.Locations.Any(l => l is { InStock: false, IsEmpty: false }))
                .FirstOrDefault(x =>
                    cacheLineCodes.Contains(x.LocationNo) && x.MatCodeList.Contains(taskInfo.MatCode) && !x.InStock);

            //这个任务可以直接去一条线体，不需要新建缓存信息
            //找到这条线体中序号最小的一条位信息 非空置且无货
            var cacheLoc = cacheLine?.Locations.Where(x => x is { InStock: false, IsEmpty: false }).MinBy(x => x.XYNo);
            if (cacheLoc != null)
            {
                cacheLoc = db.Queryable<WCS_CacheLineLoc>().Single(x => x.Id == cacheLoc.Id);
                cacheLoc.InStock = true;
                cacheLoc.TaskId = taskInfo.ID;
                cacheLoc.EditTime = nowTime;
                db.UpdateableRowLock(cacheLoc).UpdateColumns(x => new { x.InStock, x.TaskId, x.EditTime })
                    .ExecuteCommand();

                //WCS任务相关信息
                taskInfo.Status = TaskStatus.FinishOfShunt;
                taskInfo.AddrNext = cacheLine.LocationNo.ToString();
                taskInfo.EditWho = "WCS";
                taskInfo.EditTime = nowTime;
                db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EndTime })
                    .ExecuteCommand();
                taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");

                return true;
            }

            #endregion 跟据缓存信息寻找可以到达的缓存点

            #region 初始化一个信息的缓存信息

            //找到一个可以进行缓存的线体号
            var devCode = cacheLineDevList.Select(x => x.Code.ToShort()).OrderByDescending(x => x)
                .FirstOrDefault(x => !cacheLineList.Select(s => s.LocationNo).Contains(x));
            if (devCode == 0)
            {
                World.Log($"无可用线体:{taskInfo.ID}", LogLevelEnum.Mid);
                //如果当前工字轮是18，不会触发当前逻辑，因为所有在桁架码垛的18工字轮仅一种规格
                var endTime = DateTime.Now.AddMinutes(-30);
                //找一个当前时间最早且已超过30分钟未码垛的码垛信息半托入库
                //未结束且包含当前物料编号的垛形,按时间排序，创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
                var palletizing = db.Queryable<WCS_Palletizing>()
                    .Includes(x => x.Layers, r => r.Rows, l => l.Locs)
                    .Where(x => !x.Finish && x.WarehouseCode == taskInfo.WarehouseCode &&
                                x.Layers.Any(l => l.Rows.Any(r => r.Finish)))
                    .Where(x => x.EditTime < endTime).ToList()
                    .Where(x => x.Layers.SelectMany(l => l.Rows).GroupBy(r => r.MatCodeList).Count() > 1)
                    .MinBy(x => x.AddTime);
                cacheLineList = db.Queryable<WCS.Entity.WCS_CacheLine>()
                    .Where(x => x.WarehouseCode == taskInfo.WarehouseCode && x.IsTruss).ToList();

                if (palletizing == null || cacheLineList.Any()) return false;
                {
                    var dev = Device.All.Where(x => x.Code == palletizing.PalletizingStation)
                        .Select(x => new Device<ITruss530>(x, World)).FirstOrDefault();
                    if (dev != null && !dev.Data.CmdType.HasFlag(TrussCmdType.Two) && !dev.Data.CmdType.HasFlag(TrussCmdType.End1))
                    {
                        //如果找到的最早的一个托盘超六小时了，就直接强制结盘
                        dev.Data.CmdType = palletizing.AddTime < DateTime.Now.AddHours(-6) ? TrussCmdType.End1 : TrussCmdType.Two;
                    }
                }
                return false;
            }

            var result = false;

            //未结束且包含当前物料编号的垛形,按时间排序，创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
            var palletizingList = db.Queryable<WCS_Palletizing>()
                .Includes(x => x.Layers, r => r.Rows, l => l.Locs)
                .Where(x => (!x.Finish /*|| (x.Finish && x.isItHalf)*/) && x.MatCodeList.Contains(taskInfo.MatCode) &&
                            x.WarehouseCode == taskInfo.WarehouseCode)
                .OrderBy(x => x.AddTime)
                .ToList();

            foreach (var palletizing in palletizingList)
            {
                //当前任务已经分配结束，进入下一次迭代
                if (result) continue;

                //未结束且包含当前物料编号的层,按层号排序，层号小的优先分配
                var layer = palletizing.Layers.Where(x => x is { IsEmpty: false, Finish: false })
                    .Where(x => x.MatCodeList.Contains(taskInfo.MatCode)).MinBy(x => x.LayerNo);

                //未找到可用层，进入下一次迭代
                if (layer == null) continue;
                // 当前任务已经分配结束，进入下一次迭代

                //未结束，未预分配缓存线信息且包含当前物料编号的行，行号小的优先分配
                var rows = layer.Rows.Where(x => x is { IsEmpty: false, Finish: false })
                    .Where(x => x.CacheLineId == 0)
                    .Where(x => x.MatCodeList.Contains(taskInfo.MatCode))
                    .ToList();
                //未找到可用行，进入下一次迭代
                if (!rows.Any()) continue;
                var palletizingRow = rows.MinBy(x => x.RowNo);

                //开始初始化缓存位信息
                cacheLine = new WCS_CacheLine()
                {
                    LocationNo = devCode,
                    AddTime = nowTime,
                    EditTime = nowTime,
                    PalletizingRowId = palletizingRow.Id,
                    InStock = false,
                    Put = false,
                    IsTruss = false,
                    MatCodeList = palletizingRow.MatCodeList,
                    Quantity = palletizingRow.QtyMaxCount,
                    WarehouseCode = taskInfo.WarehouseCode
                };

                if (palletizing.isItHalf) //当前任务被分配到了二次码垛托盘上了。
                {
                    var twoDevCode = obj.Entity.Code switch
                    {
                        "455" => "1606",
                        "655" => "1616",
                        "855" => "1626",
                        "1055" => "1636",
                        "1255" => "1646",
                        "1455" => "1656",
                        _ => throw new ArgumentOutOfRangeException()
                    };

                    //找到一个对应的空置码垛位
                    var twoDevs = Device.All.Where(x => twoDevCode == x.Code)
                        .Select(x => new Device<IStation520, IStation521, IStation523>(x, World))
                        .Where(x => !x.Data3.Status.HasFlag(StationStatus.PH_Status)).Select(x => x.Entity.Code);
                    //找到一个对应的空置码垛位
                    var twoDev = new Device<IStation520, IStation521, IStation523>(Device.All.First(x => x.Code == twoDevCode), World);
                    if (twoDev.Data3.Status.HasFlag(StationStatus.PH_Status))
                    {
                        World.Log("无可用的二次码垛工位");
                        continue;
                    }

                    if (db.Queryable<WCS_Palletizing>().Any(x => !x.Finish && twoDevs.Contains(x.PalletizingStation)))
                    {
                        World.Log($"有正目标地址是{JsonConvert.SerializeObject(twoDevs)}的未结束二次码垛任务，无可用的二次码垛工位");
                        continue;
                    }

                    palletizing.EditTime = nowTime;
                    palletizing.Finish = false;
                    palletizing.PalletizingStation = twoDevs.FirstOrDefault();
                    palletizing.TaskAddNext = devCode.ToString();
                    db.UpdateableRowLock(palletizing).UpdateColumns(x => new { x.EditTime, x.Finish, x.PalletizingStation, x.TaskAddNext }).ExecuteCommand();
                }

                if (palletizing.TaskAddNext == null)
                {
                    palletizing.TaskAddNext = devCode.ToString();
                    db.Updateable(palletizing).UpdateColumns(x => new { x.TaskAddNext }).ExecuteCommand();
                }

                var res = db.InsertableRowLock(cacheLine).ExecuteReturnEntity();
                palletizingRow = db.Queryable<WCS_PalletizingRow>().Includes(x => x.Locs)
                    .Single(x => x.Id == palletizingRow.Id);
                palletizingRow.Locs = palletizingRow.Locs.OrderBy(x => x.XYNo).ToList();
                palletizingRow.CacheLineId = res.Id;
                palletizingRow.EditTime = nowTime;

                var createCacheLoc = palletizingRow.Locs.Select((t, i) => new WCS_CacheLineLoc()
                {
                    XYNo = t.XYNo,
                    InStock = i == 0,
                    IsEmpty = t.IsEmpty,
                    MatCode = t.MatCode,
                    TaskId = i == 0 ? taskInfo.ID : 0,
                    CacheLineId = res.Id,
                    AddTime = nowTime,
                    SideNum = t.SideNum,
                    EditTime = nowTime
                }).ToList();

                taskInfo.Status = TaskStatus.FinishOfShunt;
                taskInfo.AddrNext = devCode.ToString();
                taskInfo.EditWho = "WCS";
                taskInfo.EditTime = nowTime;
                db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EditTime })
                    .ExecuteCommand();
                taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");
                db.UpdateableRowLock(palletizingRow).UpdateColumns(x => new { x.CacheLineId, x.EditTime })
                    .ExecuteCommand();
                db.InsertableRowLock(createCacheLoc).ExecuteCommand();

                result = true;
            }

            if (!result)
            {
                taskInfo.InitStackStructure(db, World);
            }

            return result;

            #endregion 初始化一个信息的缓存信息
        }
    }
}