using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Hh.Mes.Common.config;
using Hh.Mes.Common.log;
using Infrastructure;
using SqlSugar;
using WebRepository;
using WebRepository.DomainSugars;
using WebRepository.EnumEntitys;
using Hh.Mes.Service.Repository;

namespace Hh.Mes.Service.Material
{
    public class NestingService : RepositorySqlSugar<inventory>
    {
        #region 属性
        /// <summary>
        /// 切割工单主表
        /// </summary>
        public List<cutter_head_work_order> OriginalCutterHeadWorkOrders { get; set; } = new List<cutter_head_work_order>();

        /// <summary>
        /// 切割工单子表
        /// </summary>
        public List<cutter_detail_work_order> OriginalCutterDetailWorkOrders { get; set; } = new List<cutter_detail_work_order>();

        /// <summary>
        /// 原材料 (原数据)
        /// </summary>
        public List<inventory> OriginalInventorys { get; set; } = new List<inventory>();

        /// <summary>
        /// 记录套料中明细工单 状态
        /// </summary>
        public List<cutter_detail_work_order> UpdateCutterDetails { get; set; } = new List<cutter_detail_work_order>();

        /// <summary>
        /// 记录配料单已套料的
        /// </summary>
        public List<api_material_demand_footer> MaterialDemandFooters { get; set; } = new List<api_material_demand_footer>();

        /// <summary>
        /// 记录使用中库存 修改库存状态
        /// </summary>
        public List<inventory> UpdateInventorys { get; set; } = new List<inventory>();

        public List<Record> Records { get; set; } = new List<Record>();

        /// <summary>
        /// 记录使用的工单 用于数据筛选
        /// </summary>
        public List<int> CuttingDetailId { get; set; } = new List<int>();

        public TotalModel TotalData { get; set; } = new TotalModel();
        #endregion


        #region PC页面

        /// <summary>
        /// 废料长度
        /// </summary>
        public dynamic CuttingWasteLength()
        {
            return ExceptionsHelp.Instance.ExecuteT(() =>
            {
                return GetDictionaryDictValue("废料长度", "CuttingWasteLength");
            }, catchRetrunValue: "catchRetrunValue");
        }

        /// <summary>
        /// 下料尺寸数据
        /// </summary>
        public dynamic GetInventorys()
        {
            return ExceptionsHelp.Instance.ExecuteT(() =>
            {
                OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList();
                if (OriginalInventorys.Count == 0) return ReturnTableData("原材料无", -1);
                return ReturnTableData("", 0, OriginalInventorys, OriginalInventorys.Count);
            }, catchRetrunValue: "catchRetrunValue");
        }

        /// <summary>
        /// 导入下料数据
        /// </summary>
        public dynamic GetCutterInfo()
        {
            OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(x => x.State == (int)EnumCutBodyStatus.发布).ToList();
            if (OriginalCutterDetailWorkOrders.Count == 0) return ReturnTableData("切割工单明细查询无数据", -1);
            return ReturnTableData("", 0, OriginalCutterDetailWorkOrders, OriginalCutterDetailWorkOrders.Count);
        }

        /// <summary>
        /// 套料计算
        /// </summary>
        public dynamic NestingCalculation()
        {
            OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList();
            if (OriginalInventorys.Count == 0) return ReturnTableData("原材料库存查询无数据状态", -1);

            OriginalCutterHeadWorkOrders = Context.Queryable<cutter_head_work_order>().Where(x => x.State == (int)EnumCutHeadStatus.发布 || x.State == (int)EnumCutHeadStatus.部分套料).ToList();
            if (OriginalCutterHeadWorkOrders.Count == 0) return ReturnTableData("切割工单头查询无数据状态:【发布或者已套料】", -1);

            OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(x => x.State == (int)EnumCutBodyStatus.发布).ToList();
            if (OriginalCutterDetailWorkOrders.Count == 0) return ReturnTableData("切割工单明细查询无数据状态:【发布或者已套料】", -1);

            var DetailMaterialCode = OriginalCutterDetailWorkOrders.Select(x => x.MaterialCode).Distinct().ToList();

            //如果工单中所有需要的物料,一个都不在库存中,那就提示报错返回
            foreach (var item in DetailMaterialCode)
            {
                if (OriginalInventorys.Any(x => x.materialCode == item))
                    break;
                if (item == DetailMaterialCode[DetailMaterialCode.Count - 1])
                    return ReturnTableData("没有切割工单所需的物料库存!", -1);
            }

            var inventories = new List<inventory>();
            inventories.AddRange(OriginalInventorys);

            var cutDetail = new List<cutter_detail_work_order>();
            cutDetail.AddRange(OriginalCutterDetailWorkOrders);

            var cuttingWasteLength = GetDictionaryDictValue("废料长度", "CuttingWasteLength");

            #region  切割计算

            decimal loss = 0;
            //记录切割方案 
            List<SchemeOperation> schemes = new List<SchemeOperation>();

            var CutterList = new List<List<cutter_detail_work_order>>();

            #region 数据筛选  通过切割工单的图纸以及切割工单明细的页码 进行分组操作。  LineNo,ShopDesignDrawingNo

            foreach (var order in OriginalCutterHeadWorkOrders.OrderBy(x => x.LineNo).ThenBy(x => x.ShopDesignDrawingNo))
            {
                //先添加 相同的切割工单   
                var cutters = cutDetail.Where(t => t.headKeys == order.keys).OrderBy(x => x.CuttingLength).ToList();
                CutterList.Add(cutters);
                var count = cutters.Count;
                Records.Add(new Record() { Guid = order.keys, Count = count, EidtCount = count });
            }

            #endregion

            #region 计算
            foreach (var Cutters in CutterList.OrderByDescending(x => x.Sum(t => t.CuttingLength)))
            {
                bool isok = false;
                //跳过 防止报错为null
                if (Cutters.Count == 0) continue;

                var LineCode = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Cutters.FirstOrDefault().headKeys).LineCode;
                var inventorys = inventories.Where(x => x.materialCode == Cutters.FirstOrDefault().MaterialCode && x.lineCode == LineCode).OrderBy(x => x.pipeLength).ToList();
                if (inventorys.Count == 0) continue;

                #region 不需要切割 查找直接匹配管材

                //套料过的切割工单明细
                var WorkOrderList = new List<cutter_detail_work_order>();
                foreach (var Cutter in Cutters)
                {
                    //找出相同的 刚好匹配
                    var Equalinventory = inventorys.FirstOrDefault(x => x.pipeLength == Cutter.CuttingLength);
                    if (Equalinventory != null)
                    {
                        Equalinventory.pipeLength = 0;
                        Cutter.PipeSN = Equalinventory.pipeSN;

                        //添加套料计划头
                        var cut_plan_head = new cut_plan_head();
                        cut_plan_head.PipeLength = Convert.ToDecimal(Equalinventory.pipeLength);
                        cut_plan_head.OldSN = Equalinventory.oldSN ?? Equalinventory.pipeSN;
                        cut_plan_head.PipeSN = Equalinventory.pipeSN;
                        cut_plan_head.MaterialCode = Equalinventory.materialCode;
                        cut_plan_head.InventoryId = Equalinventory.id;
                        cut_plan_head.LineCode = Equalinventory.lineCode;
                        cut_plan_head.State = (int)CutPlanExcuteStatus.初始;
                        cut_plan_head.createBy = sysUser?.Account;
                        cut_plan_head.createTime = DateTime.Now;

                        var cutPlanDetail = AddCutPlanDetail(Cutter, 0, true);

                        var scheme = schemes.Where(x => x.CutPlanHead.InventoryId == Equalinventory.id).FirstOrDefault();
                        WorkOrderList.Add(Cutter);
                        //如果管材没有套料,就添加套料头和明细,否则就添加找到的套料的明细
                        if (scheme == null)
                        {
                            scheme = new SchemeOperation();
                            scheme.CutPlanHead = cut_plan_head;
                            scheme.CutPlanDetails.Add(cutPlanDetail);
                            schemes.Add(scheme);
                        }
                        else
                        {
                            scheme.CutPlanDetails.Add(cutPlanDetail);
                        }
                    }
                }
                foreach (var WorkOrder in WorkOrderList)
                {
                    Cutters.Remove(WorkOrder);
                }

                #endregion
                //统计次数
                var CuttersCount = Cutters.Count;
                if (CuttersCount == 0) continue;

                #region 执行单管组合
                if (CuttersCount != 1)
                {
                    //从最短的管材进行配比
                    foreach (var inventory in inventorys)
                    {
                        //查询当前损耗
                        var material = Context.Queryable<material>().First(x => x.MaterialCode == inventory.materialCode);
                        loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss));

                        //损耗合计
                        var CuttersSum = Cutters.Sum(x => x.CuttingLength) + loss * CuttersCount;
                        if (inventory.pipeLength >= CuttersSum)
                        {
                            //切割方案实体
                            SchemeOperation scheme = new SchemeOperation();

                            //添加计算过的切割下料尺寸数据
                            scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(inventory.pipeLength), OldSN = inventory.oldSN ?? inventory.pipeSN, PipeSN = inventory.pipeSN, MaterialCode = inventory.materialCode, InventoryId = inventory.id, LineCode = inventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };

                            inventory.pipeLength -= CuttersSum;
                            Cutters.ForEach(t => { t.PipeSN = inventory.pipeSN; });
                            if (schemes.Any(x => x.CutPlanHead.InventoryId == inventory.id))
                            {
                                schemes.Where(x => x.CutPlanHead.InventoryId == inventory.id).FirstOrDefault().CutPlanDetails.AddRange(AddCutPlanDetail(Cutters, loss));
                            }
                            else
                            {
                                scheme.CutPlanDetails = AddCutPlanDetail(Cutters, loss);
                                schemes.Add(scheme);
                            }
                            isok = true;
                            break;
                        }
                    }
                }
                #endregion
                //false 组合没有执行成功
                if (isok) continue;

                //单管找出最短的进行配比   
                inventorys.ForEach(t =>
                {
                    //切割方案实体
                    SchemeOperation scheme = new SchemeOperation();
                    var material = Context.Queryable<material>().First(x => x.MaterialCode == t.materialCode);
                    loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss));
                    //切割方案 取原材料数据           
                    scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(t.pipeLength), OldSN = t.oldSN ?? t.pipeSN, PipeSN = t.pipeSN, MaterialCode = t.materialCode, InventoryId = t.id, LineCode = t.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };
                    for (int i = 0; i < CuttersCount; i++)
                    {
                        //没有下料切割数据那么就不用执行了
                        if (Cutters.Count == 0)
                        {
                            //跳出for循环
                            break;
                        }
                        //当前下料尺寸 拿最短的
                        var Cutter = Cutters.First();

                        //当前物料长度够 就进行切割方案添加     当不够那么后面的数据也将不会进入。 需要更换一个物料进行分配
                        if ((t.pipeLength - loss) >= Cutter.CuttingLength)
                        {
                            Cutter.PipeSN = t.pipeSN;
                            t.pipeLength -= Cutter.CuttingLength + loss;
                            if (schemes.Exists(x => x.CutPlanHead.InventoryId == t.id))
                            {
                                schemes.First(x => x.CutPlanHead.InventoryId == t.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss));
                            }
                            else
                            {
                                scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss));
                            }
                            //删除 计算后面下料数据
                            Cutters.Remove(Cutter);
                        }
                        else
                        {
                            //当前物料长度 不够 就换一个
                            break;
                        }
                    }
                    if (scheme.CutPlanDetails.Count > 0)
                    {
                        schemes.Add(scheme);
                    }
                });
            }
            #endregion

            foreach (var scheme in schemes)
            {
                scheme.CutPlanHead.LossCount = scheme.CutPlanDetails.Sum(x => x.Loss);
                var SumProducts = scheme.CutPlanDetails.Sum(x => x.Loss + x.CuttingLength);
                double Percentage = Convert.ToDouble(SumProducts) / Convert.ToDouble(scheme.CutPlanHead.PipeLength);
                scheme.Percentage = Percentage.ToString("0.00%");
                foreach (var CutPlanDetail in scheme.CutPlanDetails)
                {
                    //进行删除 切割方案当中的下料数据
                    cutDetail.RemoveAll(x => x.id == CutPlanDetail.CuttingDetailId);
                }

                #region 余料计算

                foreach (var t in inventories.Where(x => x.id == scheme.CutPlanHead.InventoryId))
                {
                    if (t.pipeLength < Convert.ToDecimal(cuttingWasteLength))
                    {
                        t.pipeSN = "废料";
                    }
                    else
                    {
                        if (t.pipeSN.Contains("-YL"))
                        {
                            var inedx = t.pipeSN.IndexOf("-YL");
                            var onepieceName = t.pipeSN.Substring(0, inedx);
                            var YLNo = t.pipeSN.Substring(t.pipeSN.Length - 2, 2);
                            if (YLNo.Contains("0"))
                            {
                                int Nos = Convert.ToInt32(YLNo) + 1;
                                t.pipeSN = onepieceName + $"-YL-0{Nos}";
                            }
                            else
                            {
                                int Nos = Convert.ToInt32(YLNo) + 1;
                                t.pipeSN = onepieceName + $"-YL-{Nos}";
                            }
                        }
                        else
                        {
                            t.oldSN = t.pipeSN;
                            t.pipeSN += "-YL-01";
                        }
                    }

                    scheme.CutPlanHead.OddmentsLength = Convert.ToDecimal(t.pipeLength);
                    scheme.CutPlanHead.OddmentsCode = t.pipeSN;
                    #region 更新当前使用的库存状态
                    t.useState = (int)InventoryPlanStatus.已经套料;
                    t.updateTime = DateTime.Now;
                    t.updateBy = sysUser?.Account;
                    UpdateInventorys.Add(t);
                    #endregion
                }

                #endregion
            }

            //切割方案
            TotalData.SchemeOperations = schemes;
            //余材料
            TotalData.Inventories = inventories.OrderBy(x => x.pipeLength).ToList();
            //未切割工单
            TotalData.CutterDetailWorkOrders = cutDetail;
            #endregion

            return ReturnTableData("计算成功!", 0, new { Middle = TotalData.SchemeOperations, RightUp = TotalData.Inventories, RightDown = TotalData.CutterDetailWorkOrders });

        }

        /// <summary>
        /// 保存套料信息
        /// </summary>
        public dynamic SaveNestingProgramme()
        {
            Init();
            var result = NestingCalculation();
            if (result.code != 0) return result;

            if (TotalData.SchemeOperations.Count == 0)
                return ReturnTableData("切割方案数据为空", -1);


            if (Context.Queryable<cut_plan_detail>().Any(x => x.CuttingDetailId == CuttingDetailId.FirstOrDefault()))
                return ReturnTableData("已添加", -1);

            #region 修改工单信息
            var UpdateCutterHead = new List<cutter_head_work_order>();

            foreach (var Record in Records)
            {
                if (Record.EidtCount == Record.Count) continue;

                var cutter = OriginalCutterHeadWorkOrders.Where(x => x.keys == Record.Guid).FirstOrDefault();

                if (Record.Count > Record.EidtCount && Record.EidtCount > 0) cutter.State = (int)EnumCutHeadStatus.部分套料;
                else if (Record.EidtCount == 0) cutter.State = (int)EnumCutHeadStatus.已套料;

                cutter.updateBy = sysUser?.Account;
                cutter.updateTime = DateTime.Now;
                UpdateCutterHead.Add(cutter);
            }

            #endregion
            #region 记录到之前余料数据当中

            var AddCutPlanDetail = new List<cut_plan_detail>();
            var AddCutPlanHead = new List<cut_plan_head>();

            foreach (var SchemeOperations in TotalData.SchemeOperations)
            {
                var guid = Guid.NewGuid();
                SchemeOperations.CutPlanHead.Keys = guid;
                SchemeOperations.CutPlanHead.createBy = sysUser?.Account;
                SchemeOperations.CutPlanHead.createTime = DateTime.Now;
                AddCutPlanHead.Add(SchemeOperations.CutPlanHead);
                SchemeOperations.CutPlanDetails.ForEach(t => { t.HeadKeys = guid; t.createBy = sysUser?.Account; t.createTime = DateTime.Now; });
                AddCutPlanDetail.AddRange(SchemeOperations.CutPlanDetails);
            }
            #endregion

            var saveResults = UpdateCuttingPlan(UpdateInventorys, UpdateCutterHead, UpdateCutterDetails, AddCutPlanHead, AddCutPlanDetail);

            if (saveResults.code != 0) return ReturnTableData($"保存失败,原因:{saveResults.msg}", -1);

            return ReturnTableData("保存成功", 0);
        }

        /// <summary>
        /// 套料保存
        /// </summary>
        /// <param name="updateInventory">修改库存状态</param>
        /// <param name="cutterHeadWorkOrder">修改切割工单主状态</param>
        /// <param name="cutterDetailWorkOrder">修改切割工单子状态</param>
        /// <param name="addcutPlans">添加方案主</param>
        /// <param name="addcutdetail">添加方案子</param>
        /// <returns></returns>
        public TableData UpdateCuttingPlan(List<inventory> updateInventory, List<cutter_head_work_order> cutterHeadWorkOrder, List<cutter_detail_work_order> cutterDetailWorkOrder, List<cut_plan_head> addcutPlans, List<cut_plan_detail> addcutdetail)
        {
            Context.Ado.BeginTran();
            var result = new TableData { code = 0, data = null, count = 0, msg = "" };
            if (Context.Updateable(updateInventory).UpdateColumns(t => new { t.useState, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新库存数据失败";
            if (Context.Updateable(cutterHeadWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新切割头表数据失败";
            if (Context.Updateable(cutterDetailWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).ExecuteCommand() <= 0) result.msg += "更新切割明细数据失败";
            if (Context.Insertable(addcutPlans).ExecuteCommand() <= 0) result.msg += "新增套料方案头表数据失败";
            if (Context.Insertable(addcutdetail).ExecuteCommand() <= 0) result.msg += "新增套料方案明细数据失败";
            if (result.msg != "")
            {
                Context.Ado.RollbackTran();
                result.code = -1;
            }
            Context.Ado.CommitTran();
            return result;
        }

        /// <summary>
        /// 切割明细  下料切割工单
        /// </summary>
        /// <param name="cutter"></param>
        /// <param name="Loss"></param>
        /// <returns></returns>
        public cut_plan_detail AddCutPlanDetail(cutter_detail_work_order cutter, decimal Loss = 0, bool Notcutting = false)
        {
            #region 切割工单状态修改
            cutter.State = (int)EnumCutHeadStatus.已套料;
            cutter.updateBy = sysUser?.Account;
            cutter.updateTime = DateTime.Now;
            UpdateCutterDetails.Add(cutter);
            CuttingDetailId.Add(cutter.id);
            Records.FirstOrDefault(x => x.Guid == cutter.headKeys).EidtCount--;
            #endregion

            cut_plan_detail cutPlanDetail = new cut_plan_detail();
            cutPlanDetail.PipeSN = cutter.PipeSN;
            cutPlanDetail.BarCode = cutter.BarCode;
            cutPlanDetail.CuttingLength = cutter.CuttingLength;
            cutPlanDetail.Loss = Loss;
            cutPlanDetail.CuttingDetailId = cutter.id;
            cutPlanDetail.CuttingHeadId = cutter.headKeys;
            cutPlanDetail.State = Notcutting ? (int)CutPlanExcuteStatus.无需切割 : (int)CutPlanExcuteStatus.初始;
            cutPlanDetail.createBy = sysUser?.Account;
            cutPlanDetail.createTime = DateTime.Now;

            return cutPlanDetail;
        }

        /// <summary>
        ///  切割明细 切割工单
        /// </summary>
        /// <param name="cutters"></param>
        /// <param name="Loss"></param>
        /// <returns></returns>
        public List<cut_plan_detail> AddCutPlanDetail(List<cutter_detail_work_order> cutters, decimal Loss = 0)
        {
            List<cut_plan_detail> list = new List<cut_plan_detail>();

            foreach (var cutter in cutters)
            {
                CuttingDetailId.Add(cutter.id);
                Records.FirstOrDefault(x => x.Guid == cutter.headKeys).EidtCount--;
                var cutt = new cut_plan_detail() { PipeSN = cutter.PipeSN, BarCode = cutter.BarCode, CuttingLength = Convert.ToInt32(cutter.CuttingLength), Loss = Loss, CuttingDetailId = cutter.id, CuttingHeadId = cutter.headKeys, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };
                list.Add(cutt);
                cutter.updateBy = sysUser?.Account;
                cutter.updateTime = DateTime.Now;
                cutter.State = (int)EnumCutHeadStatus.已套料;
                UpdateCutterDetails.Add(cutter);
            }
            return list;
        }





        /// <summary>
        /// 导出方法
        /// </summary>
        /// <returns></returns>
        public dynamic Export()
        {
            return ExceptionsHelp.Instance.ExecuteT(() =>
            {
                var result = new TableData();
                var nestingCalculation = NestingCalculation();
                List<dynamic> heads = new List<dynamic>();
                List<dynamic> details = new List<dynamic>();
                if (nestingCalculation.code > -1)
                {
                    foreach (var item in nestingCalculation.data.Middle)
                    {
                        item.CutPlanHead.Percentage = item.Percentage;
                        heads.Add(item.CutPlanHead);
                        foreach (var items in item.CutPlanDetails)
                        {
                            details.Add(items);
                        }
                    }
                }
                result.data = new
                {
                    LeftUp = GetInventorys().data,
                    LeftDown = GetCutterInfo().data,
                    Middle = nestingCalculation.code > -1 ? heads : null,
                    MiddleDesc = nestingCalculation.code > -1 ? details : null,
                    RightUp = nestingCalculation.code > -1 ? nestingCalculation.data.RightUp : null,
                    RightDown = nestingCalculation.code > -1 ? nestingCalculation.data.RightDown : null

                };
                return result;
            }, catchRetrunValue: "actionList");
        }



        /// <summary>
        /// 返回信息
        /// </summary>
        public TableData ReturnTableData(string msg = "", int? code = 0, dynamic data = null, int count = 0)
        {
            return new TableData()
            {
                code = Convert.ToInt32(code == null ? 0 : code),
                msg = msg,
                data = data,
                count = count
            };
        }
        #endregion


        #region 执行已呼叫的单管进行套料
        /// <summary>
        /// 套料计算
        /// item1=true    套料停止往下执行 ,如果为true 还要继续往下走 mes 加-$ 【面向过程遗留问题】
        /// </summary>
        public Tuple<bool, string> CalledNestingCalculation(List<Guid> bodyKeys)
        {
            #region  查询 工单物料需求表
            List<api_material_demand_footer> demandFooters;
            if (bodyKeys == null)
            {
                //执行已呼叫单管操作
                var huJiao = (int)MaterialDemandFooterState.已呼叫;
                demandFooters = base.Context.Queryable<api_material_demand_footer>()
                                     .Where(t => t.Unit.ToUpper() == "MM" && t.State == huJiao).ToList();
            }
            else
            {
                //执行选中的叫料数据套料
                var paiChan = (int)MaterialDemandFooterState.已排产;
                demandFooters = base.Context.Queryable<api_material_demand_footer>()
                                    .Where(t => t.Unit.ToUpper() == "MM" && t.State == paiChan && bodyKeys.Contains(t.bodyKeys)).ToList();
            }

            if (demandFooters.Count == 0)
            {
                Log4NetHelper.Instance.Error("CalledNestingCalculation:工单物料需求查询无数据!");
                return new Tuple<bool, string>(true, "工单物料需求查询无数据!");
            }
            MaterialDemandFooters.AddRange(demandFooters);
            #endregion

            #region 查找切割工单
            //查找工单有问题,既然用了bodyKeys,就应该按照body对应的单管号来赛选工单,如果用IWPNo来筛选范围一下扩大了,超出了bodyKeys的范围
            var iWPNos = demandFooters.Select(x => x.IWPNo).ToList();
            OriginalCutterHeadWorkOrders = Context.Queryable<cutter_head_work_order>().Where(x => x.State == (int)EnumCutHeadStatus.发布 ||
                                                                                             x.State == (int)EnumCutHeadStatus.部分套料 && iWPNos.Contains(x.IWPNo)).ToList();
            if (OriginalCutterHeadWorkOrders.Count == 0)
            {
                Log4NetHelper.Instance.Error("CalledNestingCalculation:切割工单头信息查询无数据");
                return new Tuple<bool, string>(true, "未查询到切割工单头状态【发布】的数据");
            }
            var cutterHeadKeys = OriginalCutterHeadWorkOrders.Select(x => x.keys).ToList();
            OriginalCutterDetailWorkOrders = Context.Queryable<cutter_detail_work_order>().Where(t => t.State == (int)EnumCutBodyStatus.发布 && cutterHeadKeys.Contains(t.headKeys)).ToList();
            OriginalCutterDetailWorkOrders = OriginalCutterDetailWorkOrders.Where(t => demandFooters.Exists(x => x.SpoolNo == t.SpoolNo && x.PartCode == t.PartCode &&
                                                                                                            x.ProjectCode == t.ProjectCode)).ToList();
            if (OriginalCutterDetailWorkOrders.Count == 0)
            {
                Log4NetHelper.Instance.Error("CalledNestingCalculation:下料尺寸数据无");
                return new Tuple<bool, string>(true, "下料尺寸数据(切割工单明细)信息查询无数据");
            }
            #endregion

            var detailMaterialCode = OriginalCutterDetailWorkOrders.Select(x => x.MaterialCode).Distinct().ToList();
            var isCallWms = false;
            //如果工单中所有的物料,一个都不在库存中,那就提示报错返回
            foreach (var item in detailMaterialCode)
            {
                if (OriginalInventorys.Any(x => x.materialCode == item))
                {
                    isCallWms = true;
                    break;
                }
                //if (detailMaterialCode.IndexOf(item) + 1 == detailMaterialCode.Count)
                //{
                //    Log4NetHelper.Instance.Error("没有切割工单所需的物料库存");
                //    return new Tuple<bool, string>(true, "没有切割工单所需的物料库存");
                //}
            }
            //仓库没有切割工单的物料 需要叫料仓储Wms 程序继续往下走 lsw
            if (!isCallWms)
            {
                Log4NetHelper.Instance.Error("没有切割工单所需的物料库存");
                return new Tuple<bool, string>(true, "没有切割工单所需的物料库存-$");
            }
            var inventories = new List<inventory>();
            OriginalInventorys.ForEach(i => inventories.Add(i));

            var cutDetail = new List<cutter_detail_work_order>();
            OriginalCutterDetailWorkOrders.ForEach(i => cutDetail.Add(i));

            var cuttingWasteLength = GetDictionaryDictValue("废料长度", "CuttingWasteLength");

            #region  切割计算

            decimal loss = 0;
            //记录切割方案 
            var schemes = new List<SchemeOperation>();

            var CutterList = new List<List<cutter_detail_work_order>>();

            #region 数据筛选  通过切割工单的图纸以及切割工单明细的页码 进行分组操作。  LineNo,ShopDesignDrawingNo

            foreach (var order in OriginalCutterHeadWorkOrders.OrderBy(x => x.LineNo).ThenBy(x => x.ShopDesignDrawingNo))
            {
                //先添加 相同的切割工单   
                var cutters = cutDetail.Where(t => t.headKeys == order.keys).OrderBy(x => x.CuttingLength).ToList();
                CutterList.Add(cutters);
                var count = cutters.Count;
                Records.Add(new Record() { Guid = order.keys, Count = count, EidtCount = count });
            }

            #endregion

            #region 计算
            foreach (var Cutters in CutterList.OrderByDescending(x => x.Sum(t => t.CuttingLength)))
            {
                bool isok = false;
                //跳过 防止报错为null
                if (Cutters.Count == 0) continue;

                var LineCode = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Cutters.FirstOrDefault().headKeys).LineCode;

                var materialCodes = Cutters.Select(t => t.MaterialCode).ToList();
                var inventorys = inventories.Where(x => materialCodes.Contains(x.materialCode) && x.lineCode == LineCode).OrderBy(x => x.pipeLength).ToList();

                if (inventorys.Count == 0) continue;
                #region 不需要切割 查找直接匹配管材
                var WorkOrderList = new List<cutter_detail_work_order>();
                foreach (var Cutter in Cutters)
                {
                    //找出相同的 刚好匹配
                    var Equalinventory = inventorys.FirstOrDefault(x => x.pipeLength == Cutter.CuttingLength);
                    if (Equalinventory != null)
                    {
                        SchemeOperation scheme = new SchemeOperation();
                        scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(Equalinventory.pipeLength), OldSN = Equalinventory.oldSN ?? Equalinventory.pipeSN, PipeSN = Equalinventory.pipeSN, MaterialCode = Equalinventory.materialCode, InventoryId = Equalinventory.id, LineCode = Equalinventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };
                        Equalinventory.pipeLength = 0;
                        Cutter.PipeSN = Equalinventory.pipeSN;

                        if (schemes.Any(x => x.CutPlanHead.InventoryId == Equalinventory.id))
                        {
                            schemes.FirstOrDefault(x => x.CutPlanHead.InventoryId == Equalinventory.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, 0, true));
                            WorkOrderList.Add(Cutter);
                        }
                        else
                        {
                            scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, 0, true));
                            schemes.Add(scheme);
                            WorkOrderList.Add(Cutter);
                        }
                    }
                }
                foreach (var WorkOrder in WorkOrderList)
                {
                    Cutters.Remove(WorkOrder);
                }

                #endregion
                //统计次数
                var CuttersCount = Cutters.Count;
                if (CuttersCount == 0) continue;

                #region 执行单管组合
                if (CuttersCount != 1)
                {
                    //从最短的管材进行配比
                    foreach (var inventory in inventorys)
                    {
                        //查询当前损耗
                        var material = Context.Queryable<material>().First(x => x.MaterialCode == inventory.materialCode);
                        loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss));

                        //损耗合计
                        var CuttersSum = Cutters.Sum(x => x.CuttingLength) + loss * CuttersCount;
                        if (inventory.pipeLength >= CuttersSum)
                        {
                            //切割方案实体
                            SchemeOperation scheme = new SchemeOperation();

                            //添加计算过的切割下料尺寸数据
                            scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(inventory.pipeLength), OldSN = inventory.oldSN ?? inventory.pipeSN, PipeSN = inventory.pipeSN, MaterialCode = inventory.materialCode, InventoryId = inventory.id, LineCode = inventory.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };

                            inventory.pipeLength -= CuttersSum;
                            Cutters.ForEach(t => { t.PipeSN = inventory.pipeSN; });
                            if (schemes.Any(x => x.CutPlanHead.InventoryId == inventory.id))
                            {
                                schemes.Where(x => x.CutPlanHead.InventoryId == inventory.id).FirstOrDefault().CutPlanDetails.AddRange(AddCutPlanDetail(Cutters, loss));
                            }
                            else
                            {
                                scheme.CutPlanDetails = AddCutPlanDetail(Cutters, loss);
                                schemes.Add(scheme);
                            }
                            isok = true;
                            break;
                        }
                    }
                }
                #endregion
                //false 组合没有执行成功
                if (isok) continue;

                //单管找出最短的进行配比   
                inventorys.ForEach(t =>
                {
                    //切割方案实体
                    SchemeOperation scheme = new SchemeOperation();
                    var material = Context.Queryable<material>().First(x => x.MaterialCode == t.materialCode);
                    loss = material == null ? 0 : (material.Loss == null ? 0 : Convert.ToDecimal(material.Loss));
                    //切割方案 取原材料数据           
                    scheme.CutPlanHead = new cut_plan_head() { PipeLength = Convert.ToDecimal(t.pipeLength), OldSN = t.oldSN ?? t.pipeSN, PipeSN = t.pipeSN, MaterialCode = t.materialCode, InventoryId = t.id, LineCode = t.lineCode, State = (int)CutPlanExcuteStatus.初始, createBy = sysUser?.Account, createTime = DateTime.Now };
                    for (int i = 0; i < CuttersCount; i++)
                    {
                        //没有下料切割数据那么就不用执行了
                        if (Cutters.Count == 0)
                        {
                            //跳出for循环
                            break;
                        }
                        //当前下料尺寸 拿最短的
                        var Cutter = Cutters.First();

                        //当前物料长度够 就进行切割方案添加     当不够那么后面的数据也将不会进入。 需要更换一个物料进行分配
                        if ((t.pipeLength - loss) >= Cutter.CuttingLength)
                        {
                            Cutter.PipeSN = t.pipeSN;
                            t.pipeLength -= Cutter.CuttingLength + loss;
                            if (schemes.Exists(x => x.CutPlanHead.InventoryId == t.id))
                            {
                                schemes.First(x => x.CutPlanHead.InventoryId == t.id).CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss));
                            }
                            else
                            {
                                scheme.CutPlanDetails.Add(AddCutPlanDetail(Cutter, loss));
                            }
                            //删除 计算后面下料数据
                            Cutters.Remove(Cutter);
                        }
                        else
                        {
                            //当前物料长度 不够 就换一个
                            break;
                        }
                    }
                    if (scheme.CutPlanDetails.Count > 0)
                    {
                        schemes.Add(scheme);
                    }
                });
            }
            #endregion

            foreach (var scheme in schemes)
            {
                scheme.CutPlanHead.LossCount = scheme.CutPlanDetails.Sum(x => x.Loss);
                var SumProducts = scheme.CutPlanDetails.Sum(x => x.Loss + x.CuttingLength);
                double Percentage = Convert.ToDouble(SumProducts) / Convert.ToDouble(scheme.CutPlanHead.PipeLength);
                scheme.Percentage = Percentage.ToString("0.00%");
                foreach (var CutPlanDetail in scheme.CutPlanDetails)
                {
                    //进行删除 切割方案当中的下料数据
                    cutDetail.RemoveAll(x => x.id == CutPlanDetail.CuttingDetailId);
                }

                #region 余料计算

                foreach (var t in inventories.Where(x => x.id == scheme.CutPlanHead.InventoryId))
                {
                    if (t.pipeLength < Convert.ToDecimal(cuttingWasteLength))
                    {
                        t.pipeSN = "废料";
                    }
                    else
                    {
                        if (t.pipeSN.Contains("-YL"))
                        {
                            var inedx = t.pipeSN.IndexOf("-YL");
                            var onepieceName = t.pipeSN.Substring(0, inedx);
                            var YLNo = t.pipeSN.Substring(t.pipeSN.Length - 2, 2);
                            if (YLNo.Contains("0"))
                            {
                                int Nos = Convert.ToInt32(YLNo) + 1;
                                t.pipeSN = onepieceName + $"-YL-0{Nos}";
                            }
                            else
                            {
                                int Nos = Convert.ToInt32(YLNo) + 1;
                                t.pipeSN = onepieceName + $"-YL-{Nos}";
                            }
                        }
                        else
                        {
                            t.oldSN = t.pipeSN;
                            t.pipeSN += "-YL-01";
                        }
                    }

                    scheme.CutPlanHead.OddmentsLength = Convert.ToDecimal(t.pipeLength);
                    scheme.CutPlanHead.OddmentsCode = t.pipeSN;
                    #region 更新当前使用的库存状态
                    t.useState = (int)InventoryPlanStatus.已经套料;
                    t.updateTime = DateTime.Now;
                    t.updateBy = sysUser?.Account;
                    UpdateInventorys.Add(t);
                    #endregion
                }

                #endregion
            }
            //切割方案
            TotalData.SchemeOperations = schemes;
            //余材料
            TotalData.Inventories = inventories.OrderBy(x => x.pipeLength).ToList();
            //未切割工单
            TotalData.CutterDetailWorkOrders = cutDetail;
            #endregion

            return new Tuple<bool, string>(false, "");
        }


        /// <summary>
        /// 执行已呼叫的切割工单
        /// </summary>
        public Tuple<bool, string> ExecuteCalledCuttingWorkOrder(List<Guid> bodyKeys)
        {
            Init();

            #region 库存信息查询
            OriginalInventorys = Context.Queryable<inventory>().Where(x => x.useState == (int)InventoryPlanStatus.初始 && x.status == InventoryStatus.良品).ToList();
            if (OriginalInventorys.Count == 0)
            {
                Log4NetHelper.Instance.Error("CalledNestingCalculation:库存信息需求查询无数据!");
                return new Tuple<bool, string>(false, "库存信息需求查询无数据!");
            }
            #endregion

            var result = CalledNestingCalculation(bodyKeys);
            if (result.Item1) return result;

            if (TotalData.SchemeOperations.Count == 0)
            {
                Log4NetHelper.Instance.Error("切割方案数据为空");
                return new Tuple<bool, string>(true, "切割方案数据为空");
            }
            foreach (var item in CuttingDetailId)
            {
                if (Context.Queryable<cut_plan_detail>().Any(it => it.CuttingDetailId == item))
                {
                    Log4NetHelper.Instance.Error("套料方案数据已经存在切割明细工单套料数据:" + item);
                    return new Tuple<bool, string>(true, "套料方案数据已经存在切割明细工单套料数据" + item);
                }
            }

            #region 修改工单信息
            var UpdateCutterHead = new List<cutter_head_work_order>();

            foreach (var Record in Records)
            {
                if (Record.EidtCount == Record.Count) continue;

                var cutter = OriginalCutterHeadWorkOrders.FirstOrDefault(x => x.keys == Record.Guid);

                if (Record.Count > Record.EidtCount && Record.EidtCount > 0) cutter.State = (int)EnumCutHeadStatus.部分套料;
                else if (Record.EidtCount == 0) cutter.State = (int)EnumCutHeadStatus.已套料;

                cutter.updateBy = sysUser?.Account;
                cutter.updateTime = DateTime.Now;
                UpdateCutterHead.Add(cutter);
            }

            #endregion

            #region 记录到之前余料数据当中

            var AddCutPlanDetail = new List<cut_plan_detail>();
            var AddCutPlanHead = new List<cut_plan_head>();

            foreach (var SchemeOperations in TotalData.SchemeOperations)
            {
                var guid = Guid.NewGuid();
                SchemeOperations.CutPlanHead.Keys = guid;
                SchemeOperations.CutPlanHead.createBy = sysUser?.Account;
                SchemeOperations.CutPlanHead.createTime = DateTime.Now;
                AddCutPlanHead.Add(SchemeOperations.CutPlanHead);
                SchemeOperations.CutPlanDetails.ForEach(t => { t.HeadKeys = guid; t.createBy = sysUser?.Account; t.createTime = DateTime.Now; });
                AddCutPlanDetail.AddRange(SchemeOperations.CutPlanDetails);
            }
            #endregion

            #region 修改配料单明细状态改为已套料
            var demand_Footers = MaterialDemandFooters.Where(x => UpdateCutterDetails.Exists(t => t.SpoolNo == x.SpoolNo && t.PartCode == x.PartCode));
            //demand_Footers.ForEach(t => t.State = (int)MaterialDemandFooterState.已套料);
            var demandfooters = demand_Footers.Where(t => t.State == (int)MaterialDemandFooterState.已套料).ToList();
            #endregion

            var resultSave = UpdateCalledCuttingPlan(UpdateInventorys, UpdateCutterHead, UpdateCutterDetails, AddCutPlanHead, AddCutPlanDetail, demandfooters);
            return resultSave;
        }

        /// <summary>
        /// 套料保存
        /// </summary>
        /// <param name="updateInventory">修改库存状态</param>
        /// <param name="cutterHeadWorkOrder">修改切割工单主状态</param>
        /// <param name="cutterDetailWorkOrder">修改切割工单子状态</param>
        /// <param name="addcutPlans">添加方案主</param>
        /// <param name="addcutdetail">添加方案子</param>
        /// <param name="demand_Footers"></param>
        /// <returns></returns>
        public Tuple<bool, string> UpdateCalledCuttingPlan(List<inventory> updateInventory, List<cutter_head_work_order> cutterHeadWorkOrder,
                                                           List<cutter_detail_work_order> cutterDetailWorkOrder, List<cut_plan_head> addcutPlans,
                                                           List<cut_plan_detail> addcutdetail, List<api_material_demand_footer> demand_Footers)
        {
            //更新库存数据失败
            Context.Updateable(updateInventory).UpdateColumns(t => new { t.useState, t.updateBy, t.updateTime }).AddQueue();
            //更新切割头表数据失败
            Context.Updateable(cutterHeadWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).AddQueue();
            //更新切割明细数据失败
            Context.Updateable(cutterDetailWorkOrder).UpdateColumns(t => new { t.State, t.updateBy, t.updateTime }).AddQueue();
            //新增套料方案头表数据失败
            Context.Insertable(addcutPlans).AddQueue();
            //新增套料方案明细数据失败
            Context.Insertable(addcutdetail).AddQueue();
            //修改配料单明显状态失败
            Context.Updateable(demand_Footers).AddQueue();
            Context.SaveQueues();
            return new Tuple<bool, string>(false, "套料保存");
        }

        #endregion

        public void Init()
        {
            OriginalCutterHeadWorkOrders.Clear();
            OriginalCutterDetailWorkOrders.Clear();
            OriginalInventorys.Clear();
            UpdateCutterDetails.Clear();
            UpdateInventorys.Clear();
            Records.Clear();
            CuttingDetailId.Clear();
            MaterialDemandFooters.Clear();
        }
    }
}