using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Extensions;
using Siger.Middlelayer.Common.FieldEnum;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Repository.Paged;
using Siger.Middlelayer.IMSRepository.Entities;
using Siger.Middlelayer.IMSRepository.Repositories.Interface;
using Siger.Middlelayer.IMSRepository.Response;
using Siger.Middlelayer.WmsRepository.Entities;

namespace Siger.Middlelayer.IMSRepository.Repositories
{
    internal class ToolLifeToolChoiceRepository : IMSRepositoryBase<siger_project_ims_tool_choice>,
        IToolLifeToolChoiceRepository
    {
        private readonly object _lockObj = new object();
        private readonly ApiIMSDbContext _context;

        public ToolLifeToolChoiceRepository(ApiIMSDbContext context) : base(context)
        {
            _context = context;
        }

        public IEnumerable<ResponseGetToolChoice> GetPagedChoice(int workorder_id, int techId, int count, int projectId)
        {
            var result = new List<ResponseGetToolChoice>();
            var querylist = from t1 in _context.siger_project_ims_tool
                join q in _context.siger_project_ims_tool_choice on t1.material_id equals q.material_id
                where q.project_id == projectId && q.status == (int) RowState.Valid 
                      //&&t1.category != ImsSettingCategory.Attachment
                      && q.workorder_id == workorder_id && q.technology_id == techId
                select new ResponseGetToolChoice
                {
                    cutter_number = q.cutter_number,
                    tool_id = t1.id > 0 ? t1.id : 0,
                    tool_number = t1.name,
                    tool_category = t1.category,
                    confirm_status = q.choice_status,
                    choice_id = q.id
                };
            var temp = new List<ResponseGetToolChoice>();
            foreach (var choice in querylist.OrderBy(m => m.cutter_number).ToList())
            {
                if (temp.Select(m => m.cutter_number).Contains(choice.cutter_number))
                {
                    continue;
                }

                var choices = querylist.Where(q => q.cutter_number == choice.cutter_number);
                var entity = new ResponseGetToolChoice
                {
                    cutter_number = choice.cutter_number,
                    confirm_status = choice.confirm_status,
                    choice_id = choice.choice_id
                };
                var tool = choices.FirstOrDefault(m => m.tool_category == ImsSettingCategory.Mould);
                if (tool != null)
                {
                    entity.tool_id = tool.tool_id;
                    entity.tool_number = tool.tool_number;
                }

                var handler = choices.FirstOrDefault(m => m.tool_category == ImsSettingCategory.Core);
                if (handler != null)
                {
                    entity.handler_id = handler.tool_id;
                    entity.handler_number = handler.tool_number;
                }

                temp.Add(entity);
            }

            foreach (var choice in temp)
            {
                for (int i = 0; i < count; i++)
                {
                    result.Add(choice);
                }
            }

            var entities = result.ToList();
            return entities;
        }

        public IEnumerable<ResponseGetStockFromWms> GetStockFromWms(IEnumerable<string> partNos, int projectId)
        {
            var response = new List<ResponseGetStockFromWms>();

            var stocks = from s in _context.siger_wms_stock
                join lo in _context.siger_wms_storage_location on s.storage_location_id equals lo.id
                join st in _context.siger_wms_storage on lo.storageid equals st.id
                join bu in _context.siger_wms_bussinese_contacts on s.businessid equals bu.id
                where partNos.Contains(s.material_pn) && s.projectid == projectId &&
                      s.stock_state == (int) StockEnum.InWavehouse
                      && s.quantity > 0
                select new ResponseGetStockFromWms
                {
                    supplier = bu.name,
                    stock_num = s.quantity,
                    category = s.material_pn,
                    warehose = st.name,
                    supplier_id = s.businessid,
                    storage_location_id = s.storage_location_id
                };
            if (stocks.Any())
            {
                var groups = from p in stocks
                    group p by new
                    {
                        p.supplier,
                        p.warehose,
                        p.supplier_id,
                        p.storage_location_id,
                        p.category
                    }
                    into g
                    select g;
                foreach (var stock in groups)
                {
                    response.Add(new ResponseGetStockFromWms
                    {
                        supplier = stock.Key.supplier,
                        warehose = stock.Key.warehose,
                        storage_location_id = stock.Key.storage_location_id,
                        supplier_id = stock.Key.supplier_id,
                        category = stock.Key.category,
                        stock_num = stock.Sum(q => q.stock_num)
                    });
                }
            }

            return response;
        }

        public IEnumerable<ResponseGetStockFromWms> GetStockInfoes(IEnumerable<int> choiceIds, int projectId)
        {
            var stocks = from s in _context.siger_project_ims_tool_choice
                join t in _context.siger_project_ims_tool on s.material_id equals t.material_id
                join sk in _context.siger_wms_stock on s.material_id equals sk.material_id
                join lo in _context.siger_wms_storage_location on sk.storage_location_id equals lo.id
                join st in _context.siger_wms_storage on lo.storageid equals st.id
                where choiceIds.Contains(s.id) && s.project_id == projectId
                select new ResponseGetStockFromWms
                {
                    supplier = "",//需要改
                    stock_num = s.quantity,
                    warehose = st.name,
                    category = t.name,
                    type = (int) t.category,
                    tool_id = t.id > 0 ? t.id : 0
                };
            return stocks.AsEnumerable();
        }

        public bool OutStock(IEnumerable<int> choiceIds, int projectId, int userId)
        {
            var stocks = from s in _context.siger_project_ims_tool_choice_summary
                join t in _context.siger_project_ims_tool on s.material_id equals t.material_id
                join sk in _context.siger_wms_stock on s.material_id equals sk.material_id
                join lo in _context.siger_wms_storage_location on sk.storage_location_id equals lo.id
                where choiceIds.Contains(s.id) && s.project_id == projectId
                select new
                {
                    s.id,
                    pn = t.part_no,
                    s.supplier_id,
                    wareHouseId = lo.storageid,
                    s.quantity,
                    s.workorder_id
                };
            var choices = stocks.GroupBy(t => t.id).Select(t => t.FirstOrDefault()).ToList();
            if (!choices.Any())
            {
                throw new BadRequestException(RequestEnum.NoChoiceTools);
            }

            foreach (var wareHouseId in choices.Select(m => m.wareHouseId).Distinct())
            {                
                var billCode = GetBillId("1", projectId);
                var entity = new siger_wms_stock_access_order
                {
                    contactid = 0,
                    category_code = (int)WaveHousingType.ProductionOut,
                    category = EnumHelper.GetEnumDesc(WaveHousingType.ProductionOut),
                    storageid = wareHouseId,
                    access_type = (int) WaveHouseType.Out,
                    order_number = billCode,
                    origin_order_number = billCode,
                    order_status = (int) WaveHousingState.Waiting,
                    creator = userId,
                    create_time = DateTime.Now,
                    update_time = DateTime.Now,
                    updator = userId,
                    projectid = projectId,
                    status = (int) RowState.Valid,
                    picker = userId
                };
                _context.siger_wms_stock_access_order.Add(entity);
                if (_context.SaveChanges() < 0)
                {
                    return false;
                }

                var wareChoices = choices.Where(q => q.wareHouseId == wareHouseId);
                foreach (var stock in wareChoices)
                {
                    var material = _context.siger_tr_materials.FirstOrDefault(f =>
                        f.pn == stock.pn && f.projectId == projectId && f.status == (int) RowState.Valid);
                    if (material == null)
                    {
                        continue;
                    }

                    var detail = new siger_wms_stock_access_order_detail
                    {
                        orderid = entity.id,
                        material_id = material.id,
                        material_name = material.name,
                        material_pn = material.pn,
                        material_spec = material.spec,
                        manage_mode = material.manage_mode,
                        unit = GetUnitName(material.unit, projectId),
                        quantity = stock.quantity,
                        creator = userId,
                        create_time = DateTime.Now,
                        update_time = DateTime.Now,
                        updator = userId,
                        projectid = projectId,
                        businessid = stock.supplier_id,
                        status = (int) RowState.Valid
                    };

                    _context.siger_wms_stock_access_order_detail.Add(detail);

                    //修改状态
                    var choice = _context.siger_project_ims_tool_choice.FirstOrDefault(q => q.id == stock.id);
                    if (choice != null)
                    {
                        choice.choice_status = ChoiceStatus.HasApply;
                        _context.siger_project_ims_tool_choice.Update(choice);
                    }
                }
            }
            var workorderid = choices.FirstOrDefault().workorder_id;
            var workOrder = Queryable.FirstOrDefault<siger_project_ims_workorder>(_context.siger_project_ims_workorder, t => t.id == workorderid);
            if (workOrder == null)
            {
                throw new BadRequestException(RequestEnum.WorkOrderIsNotExist);
            }
            else
            {
                workOrder.operator_id = userId;
                workOrder.order_status = Common.FieldEnum.OrderStatus.Completed;
                _context.siger_project_ims_workorder.Update(workOrder);
            }
            return _context.SaveChanges() > 0;
        }

        public bool OutStock(List<siger_project_ims_tool_choice_summary> choices, int projectId, int userId)
        {
            var stocks = new List<StockOutData>();
            foreach (var item in choices)
            {
                var model = new StockOutData
                {
                    id=item.id,
                    supplier_id=item.supplier_id,
                    quantity=item.quantity,
                    workorder_id=item.workorder_id,
                    serial_no=item.serial_no
                };
                var stockdetail = _context.siger_wms_stock_detail.First(f => f.projectid == projectId && (f.serialNumber == model.serial_no || f.batch == model.serial_no));
                model.wareHouseId = _context.siger_wms_storage_location.First(f => f.id == stockdetail.locationid)?.storageid ?? 0;
                model.pn = stockdetail.inventorySN;
                model.stockNo = stockdetail.stockNo;
                model.material_id = stockdetail.inventory;
                stocks.Add(model);
            }
            var warehouseIds = stocks.Select(s => s.wareHouseId).Distinct();
            if (!warehouseIds.Any())
            {
                return false;
            }
            //init access_order stock_detail
            foreach (var warehouseId in warehouseIds)
            {
                var billCode = GetBillId("1", projectId);
                var entity = new siger_wms_stock_access_order
                {
                    contactid = 0,
                    category_code = (int)WaveHousingType.ProductionOut,
                    category = EnumHelper.GetEnumDesc(WaveHousingType.ProductionOut),
                    storageid = warehouseId,
                    access_type = (int)WaveHouseType.Out,
                    order_number = billCode,
                    origin_order_number = billCode,
                    order_status = (int)WaveHousingState.WaveHousingOutFinish,
                    creator = userId,
                    create_time = DateTime.Now,
                    update_time = DateTime.Now,
                    updator = userId,
                    projectid = projectId,
                    status = (int)RowState.Valid,
                    picker = userId,
                    auditor = userId,
                    audit_desc = string.Empty,
                    audit_fail_reason = string.Empty,
                    audit_time = DateTime.Now,
                    departid = 0,
                    option = string.Empty,
                };
                _context.siger_wms_stock_access_order.Add(entity);
                if (_context.SaveChanges() < 0)
                {
                    return false;
                }
                var access_choices = stocks.Where(f => f.wareHouseId == warehouseId);
                foreach (var item in access_choices)
                {
                    var stock_detail = _context.siger_wms_stock_detail.FirstOrDefault(f => f.projectid == projectId && (f.serialNumber == item.serial_no || f.batch == item.serial_no));
                    if (stock_detail == null)
                    {
                        continue;
                    }
                    var material = _context.siger_tr_materials.FirstOrDefault(f =>
                        f.id == stock_detail.inventory && f.projectId == projectId && f.status == (int)RowState.Valid);
                    if (material == null)
                    {
                        continue;
                    }
                    var detail = new siger_wms_stock_access_order_detail
                    {
                        orderid = entity.id,
                        material_id = stock_detail.inventory,
                        material_name = stock_detail.inventoryName,
                        material_pn = stock_detail.inventorySN,
                        material_spec = stock_detail.inventorySpec,
                        manage_mode = material.manage_mode,
                        unit = GetUnitName(material.unit, projectId),
                        quantity = stock_detail.qty,
                        creator = userId,
                        create_time = DateTime.Now,
                        update_time = DateTime.Now,
                        updator = userId,
                        projectid = projectId,
                        businessid = stock_detail.businessid,
                        status = (int)RowState.Valid,
                        access_quantity = item.quantity,
                        auditor = userId,
                        audit_desc = string.Empty,
                        audit_fail_reason = string.Empty,
                        audit_time = DateTime.Now,
                        storage_location_id = stock_detail.locationid,
                    };
                    _context.siger_wms_stock_access_order_detail.Add(detail);
                    var stockDetailModel = new siger_wms_stock_detail
                    {
                        type = (int)traceType.WaveHousingOut,
                        billID = entity.order_number,
                        inventory = stock_detail.inventory,
                        inventoryName = stock_detail.inventoryName,
                        inventorySN = stock_detail.inventorySN,
                        inventorySpec = stock_detail.inventorySpec,
                        allqty = detail.quantity,
                        qty = detail.access_quantity,
                        userid = userId,
                        username = _context.siger_project_user.FirstOrDefault(f => f.mid == userId)?.name ?? "",
                        updatetime = DateTime.Now,
                        state = (int)RowState.Valid,
                        locationid = stock_detail.locationid,
                        locationname = stock_detail.locationname,
                        projectid = projectId,
                        stockNo = item.stockNo,
                        businessid = stock_detail.businessid,
                        businessName = stock_detail.businessName,
                    };
                    if (material.manage_mode.ToInt() == (int)managemodel.Batch)
                    {
                        stockDetailModel.batch = item.serial_no;
                        detail.batch_number = item.serial_no;
                    }
                    else if (material.manage_mode.ToInt() == (int)managemodel.No)
                    {
                        stockDetailModel.no = item.serial_no;
                        detail.serial_number = item.serial_no;
                    }
                    _context.siger_wms_stock_detail.Add(stockDetailModel);

                    var stockModel = _context.siger_wms_stock.FirstOrDefault(f => f.stockNo == item.stockNo && f.projectid == projectId);
                    if (stockModel == null)
                    {
                        throw new BadRequestException(RequestEnum.StockNotExist);
                    }
                    stockModel.quantity -= detail.access_quantity;
                    //更新状态为出库
                    if (stockModel.quantity == 0)
                    {
                        stockModel.stock_state = (int)StockEnum.OutWavehouse;
                    }
                    _context.siger_wms_stock.Update(stockModel);

                    //汇总库存
                    SubtractStockChange(entity.storageid, detail.material_id, detail.access_quantity, projectId);
                }
                if (_context.SaveChanges() < 0)
                {
                    return false;
                }
            }
            var workData = _context.siger_project_ims_workorder.FirstOrDefault(f => f.id == choices.First().workorder_id);
            if (workData == null)
            {
                throw new BadRequestException(RequestEnum.WorkOrderIsNotExist);
            }
            workData.operator_id = userId;
            workData.order_status = Common.FieldEnum.OrderStatus.Completed;
            _context.siger_project_ims_workorder.Update(workData);
            if (_context.SaveChanges() < 0)
            {
                return false;
            }
            //init stock_alarm
            foreach (var material_id in stocks.Select(s=>s.material_id).Distinct())
            {
                var material = _context.siger_tr_materials.FirstOrDefault(f => f.projectId == projectId && f.status != 0 && f.id == material_id);
                var stock_Alarm = _context.siger_wms_stock_alarm.FirstOrDefault(t => t.material_id == material.id && t.projectid == projectId
                        && t.status == (int)AlarmStatus.Operating);
                if (stock_Alarm != null)
                {
                    stock_Alarm.trigger_time = UnixTimeHelper.GetNow();
                    _context.siger_wms_stock_alarm.Update(stock_Alarm);
                    _context.SaveChanges();
                    continue;
                }
                var stockTotal = _context.siger_wms_stock.Where(t => t.material_id == material_id && t.status == (int)RowState.Valid &&
                        t.projectid == projectId && t.stock_state == (int)StockEnum.InWavehouse).Sum(t => t.quantity);
                if (stockTotal < material.min_stock || (material.min_stock == 0 && stockTotal == material.min_stock))
                {
                    var alarm = new siger_wms_stock_alarm
                    {
                        material_id = material.id,
                        material_pn = material.pn,
                        information_type = (int)AlarmInfoType.StockAlarm,
                        creator = userId,
                        create_time = UnixTimeHelper.GetNow(),
                        projectid = projectId,
                        status = (int)RowState.Valid,
                        trigger_time = UnixTimeHelper.GetNow()
                    };
                    _context.siger_wms_stock_alarm.Add(alarm);
                    _context.SaveChanges();
                }
            }
            return true;
        }

        public void SubtractStockChange(int storage, int inventoryid, int count, int projectid)
        {
            var start = DateTime.Now.Date;
            var entity = _context.siger_wms_stock_change.FirstOrDefault(f => f.time >= start && f.state == (int)RowState.Valid && f.projectid == projectid && f.storageid == storage && f.inventoryid == inventoryid);
            if (entity == null)
            {
                int tmpCount = 0;
                entity = _context.siger_wms_stock_change.Where(f => f.state == (int)RowState.Valid && f.projectid == projectid && f.storageid == storage &&
                f.inventoryid == inventoryid).OrderByDescending(f => f.time).FirstOrDefault();
                if (entity == null)
                {
                    return;
                }

                tmpCount = entity.count - count;
                if (tmpCount < 0)
                {
                    return;
                }
                entity = new siger_wms_stock_change
                {
                    storageid = storage,
                    inventoryid = inventoryid,
                    count = tmpCount,
                    time = DateTime.Now,
                    projectid = projectid,
                    state = (int)RowState.Valid
                };
                _context.siger_wms_stock_change.Add(entity);
            }
            else
            {
                entity.count -= count;
                if (entity.count < 0)
                {
                    return;
                }
                _context.siger_wms_stock_change.Update(entity);
            }
        }

        public bool InStock(IEnumerable<siger_project_ims_package> packages, int storage_id, int projectId, int userId)
        {
            //var stocks = from s in packages
            //    join c in _context.siger_project_ims_tool_choice on s.choice_id equals c.id
            //    join t in _context.siger_project_ims_tool on s.handler_id equals t.id
            //    where s.project_id == projectId
            //    select new
            //    {
            //        s.id,
            //        pn = t.part_no,
            //        c.quantity,
            //        c.supplier_id//需要改
            //    };
            //var choices = stocks.ToList();
            //if (!choices.Any())
            //{
            //    throw new BadRequestException(RequestEnum.NoChoiceTools);
            //}

            //var billCode = GetBillId("0", projectId);
            //var entity = new siger_wms_stock_access_order
            //{
            //    contactid = 0,
            //    category = EnumHelper.GetEnumDesc(WaveHousingType.WholeToolIn),
            //    storageid = storage_id,
            //    access_type = (int) WaveHouseType.In,
            //    order_number = billCode,
            //    origin_order_number = billCode,
            //    order_status = (int) WaveHousingState.Waiting,
            //    creator = userId,
            //    create_time = DateTime.Now,
            //    update_time = DateTime.Now,
            //    updator = userId,
            //    projectid = projectId,
            //    status = (int) RowState.Valid
            //};
            //_context.siger_wms_stock_access_order.Add(entity);
            //if (_context.SaveChanges() < 0)
            //{
            //    return false;
            //}

            ////按照料号和供应商分组入库
            //foreach (var supplierId in choices.Select(m => m.supplier_id).Distinct())//需要改？物料和供应商是一对多的关系，而仓库里面只能存一个，具体情况需要继续分析仓库系统的处理逻辑
            //{
            //    var supplierChoices = choices.Where(q => q.supplier_id == supplierId).ToList();
            //    foreach (var pn in supplierChoices.Select(m => m.pn).Distinct())
            //    {
            //        var material = _context.siger_tr_materials.FirstOrDefault(f => f.pn == pn && f.projectId == projectId && f.status == (int)RowState.Valid);
            //        if (material == null)
            //        {
            //            continue;
            //        }

            //        var quantity = supplierChoices.Count(m => m.pn == pn);
            //        var detail = new siger_wms_stock_access_order_detail
            //        {
            //            orderid = entity.id,
            //            material_id = material.id,
            //            material_name = material.name,
            //            material_pn = material.pn,
            //            material_spec = material.spec,
            //            manage_mode = material.manage_mode,
            //            unit = GetUnitName(material.unit, projectId),
            //            quantity = quantity,
            //            creator = userId,
            //            create_time = DateTime.Now,
            //            update_time = DateTime.Now,
            //            updator = userId,
            //            projectid = projectId,
            //            businessid = supplierId,
            //            status = (int)RowState.Valid
            //        };

            //        _context.siger_wms_stock_access_order_detail.Add(detail);
            //    }

            //    foreach (var stock in supplierChoices)
            //    {
            //        //修改状态
            //        var package = _context.siger_project_ims_package.FirstOrDefault(q => q.id == stock.id);
            //        if (package != null)
            //        {
            //            package.confirm_status = ChoiceStatus.HasApply;
            //            _context.siger_project_ims_package.Update(package);
            //        }
            //    }
            //}

            //return _context.SaveChanges() > 0;

            return true;
        }

        private string GetUnitName(string unit, int projectId)
        {
            var entity = Queryable.FirstOrDefault<SigerTrDict>(_context.siger_tr_dict, f => f.status == (int)RowState.Valid 
                                                                    && f.cat == DictUnitType.meterunits.ToString() 
                                                                    && f.dkey == unit && f.projectId == projectId);
            return entity?.dval;
        }

        private string GetBillId(string key, int projectid)
        {
            var tmp = _context.siger_wms_settings.FirstOrDefault(f => f.key == key && f.projectid == projectid);
            var value = "";
            switch (key)
            {
                case "0":
                    value = tmp == null ? "SC" : tmp.value;
                    break;
                case "1":
                    value = tmp == null ? "SH" : tmp.value;
                    break;
            }

            siger_wms_id t;
            lock (_lockObj)
            {
                t = new siger_wms_id
                {
                    day = DateTime.Now,
                    type = 1
                };
                _context.siger_wms_id.Add(t);
                _context.SaveChanges();

                var count = _context.siger_wms_id.Count(f => f.day > DateTime.Now.Date && f.day <= DateTime.Now.AddDays(1).Date && f.type == 1);
                t.code = $"{count:D6}";
                _context.siger_wms_id.Update(t);
                _context.SaveChanges();
            }

            var result = $"{value}{DateTime.Now:yyyyMMdd}{t.code}";
            return result;
        }

        public IPagedCollectionResult<ResponseGetTechToolList> GetPageList(int techId, int workorder_id, string cutterNumber, int page, int pagesize,
            int toexcel, int projectId)
        {
            var querylist = from q in _context.siger_project_ims_tool_choice
                            join tech in _context.siger_project_ims_technology on q.technology_id equals tech.id
                            join m in _context.siger_tr_materials on q.material_id equals m.id
                            join t in _context.siger_project_ims_tool on q.material_id equals t.material_id
                            join c in _context.siger_project_ims_category on t.category_id equals c.id
                            where q.status == (int)RowState.Valid && q.project_id == projectId && q.technology_id == techId
                                && q.workorder_id == workorder_id
                            select new ResponseGetTechToolList
                            {
                                id = q.id,
                                remark = q.remark,
                                cutter_number = q.cutter_number,
                                part_no = q.part_no,
                                material_name = m.name ?? "",
                                supplier = m.supplier ?? "",
                                quantity = q.quantity,
                                category_name = c.name ?? "",
                                material_id = q.material_id,
                                product_id = tech.product_id,
                                route_id = tech.route_id
                            };

            Expression<Func<ResponseGetTechToolList, bool>> cutterNumberExpression = q => true;
            if (!string.IsNullOrWhiteSpace(cutterNumber))
            {
                cutterNumberExpression = q => q.cutter_number == cutterNumber;
            }
            if (toexcel == 0)
            {
                var totalCount = querylist.GroupBy(t => t.id).Select(t => t.FirstOrDefault()).Count(cutterNumberExpression);
                var entities = querylist.Where(cutterNumberExpression).GroupBy(t => t.id)
                    .Select(t => t.FirstOrDefault()).Skip((page - 1) * pagesize).Take(pagesize).AsNoTracking().ToList();
                return new PagedCollectionResult<ResponseGetTechToolList>(entities, totalCount);
            }
            else
            {
                var entities = querylist.Where(cutterNumberExpression).GroupBy(t => t.id).Select(t => t.FirstOrDefault()).AsNoTracking().ToList();
                return new PagedCollectionResult<ResponseGetTechToolList>(entities, 0);
            }
        }
    }
}
