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;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Repository.Extensions;
using Siger.Middlelayer.Repository.Response;
using Siger.Middlelayer.TlmRepository.Entities;
using Siger.Middlelayer.TlmRepository.Request;
using Siger.Middlelayer.TlmRepository.Response;
using Siger.Middlelayer.WmsRepository.Entities;

namespace Siger.Middlelayer.TlmRepository.Repositories
{
    internal class TlmRepositoryBase<TEntity> : RepositoryBase<TEntity> where TEntity : TlmEntityBase
    {
        private readonly ApiTlmDbContext _context;
        public TlmRepositoryBase(ApiTlmDbContext context) : base(context)
        {
            _context = context;
        }

        public IEnumerable<siger_project_toollife_category> GetAllCategories(int projectid, TlmSettingCategory type)
        {
            var list = new List<siger_project_toollife_category>();
            var query = GetSonCategories(0, projectid, type);

            foreach (var section in query.ToList())
            {
                list.Add(section);
            }

            return list;
        }

        public IEnumerable<siger_project_toollife_category> GetSonCategories(int parentId, int projectid, TlmSettingCategory type)
        {
            var query = from c in _context.siger_project_toollife_category
                        where c.parent == parentId && c.project_id == projectid && c.status == (int)RowState.Valid && c.categorys == type
                        select c;

            return query.ToList().Concat(query.ToList().SelectMany(t => GetSonCategories(t.id, projectid, type)));
        }

        public IEnumerable<ResponseGetMaterials> GetMaterials(string category, string name, string cutter_number, string product_id, string route, int projectid)
        {
            var queryList = from q in _context.siger_tr_materials
                            join c in _context.siger_tr_material_type on q.typeid equals c.id
                            join t in _context.siger_project_toollife_tool on q.id equals t.material_id
                            where q.status == (int)RowState.Valid && q.projectId == projectid && c.status == (int)RowState.Valid
                                  && t.status == (int)RowState.Valid && t.project_id == projectid
                            select new ResponseGetMaterials
                            {
                                id = q.id,
                                category = c.name,
                                name = q.name,
                                part_no = q.pn,
                                supplier = string.Empty
                            };
            Expression<Func<ResponseGetMaterials, bool>> categoryExpression = q => true;
            if (!string.IsNullOrWhiteSpace(category))
            {
                categoryExpression = q => q.category.Contains(category);
            }
            Expression<Func<ResponseGetMaterials, bool>> nameExpression = q => true;
            if (!string.IsNullOrWhiteSpace(name))
            {
                nameExpression = q => q.name.Contains(name);
            }

            var expression = categoryExpression.And(nameExpression);
            var dataList = queryList.Where(expression).GroupBy(t => t.id).Select(t => t.FirstOrDefault()).ToList();
            var resList = new List<ResponseGetMaterials>();
            var tooltechs = new List<siger_project_toollife_tech_tool>();
            if (!string.IsNullOrWhiteSpace(cutter_number) && product_id.ToInt() > 0 && route.ToInt() > 0)
            {
                var tech = Queryable.FirstOrDefault<siger_project_toollife_technology>(_context.siger_project_toollife_technology, t =>
                    t.product_id == product_id.ToInt() && t.route_id == route.ToInt() && t.project_id == projectid &&
                    t.status == (int)RowState.Valid);
                if (tech != null)
                {
                    tooltechs = Queryable.Where<siger_project_toollife_tech_tool>(_context.siger_project_toollife_tech_tool, t =>
                        t.project_id == projectid && t.status == (int)RowState.Valid &&
                        t.cutter_number == cutter_number &&
                        t.technology_id == tech.id).ToList();
                }
            }
            foreach (var data in dataList)
            {
                var suppliers = from q in _context.siger_tr_material_supplier
                                join c in _context.siger_wms_bussinese_contacts on q.supplier_id equals c.id
                                where q.projectId == projectid && q.status == (int)RowState.Valid && c.status == (int)RowState.Valid
                                      && q.material_id == data.id
                                select new
                                {
                                    c.name
                                };
                data.supplier = string.Join(",", suppliers.Select(t => t.name).ToList());
                var techtool = tooltechs.FirstOrDefault(t => t.part_no == data.part_no);
                data.isselected = techtool == null ? 0 : 1;
                data.quantity = techtool?.quantity ?? 0;
                resList.Add(data);
            }

            return resList;
        }

        public IEnumerable<ResponseGetMaterials> GetMaterials(RequestGetMaterial request)
        {
            var projectId = request.projectid;
            var product_id = request.product_id.ToInt();
            var route = request.route.ToInt();
            List<int> materialids;
            if (!string.IsNullOrEmpty(request.tool_code))
            {
                var config = _context.ProjectToollifeToolConfigEntities.Where(f => f.Code == request.tool_code && f.Projectid == projectId && f.Status != 0);
                if (config.Any())
                {
                    var toolids = config.Select(s => s.ToolId).ToList();
                    materialids = _context.siger_project_toollife_tool.Where(f => f.project_id == projectId && f.status != 0 && toolids.Contains(f.id)).Select(s => s.material_id).Distinct().ToList();
                }
                else
                {
                    materialids = new List<int>();
                }
            }
            else
            {
                materialids = _context.siger_project_toollife_tool.Where(f => f.project_id == projectId && f.status != 0).Select(s => s.material_id).Distinct().ToList();
            }
            var queryList = from q in _context.siger_tr_materials
                            join c in _context.siger_tr_material_type on q.typeid equals c.id
                            where q.status == (int)RowState.Valid && q.projectId == projectId && c.status == (int)RowState.Valid
                            && materialids.Contains(q.id)
                            select new ResponseGetMaterials
                            {
                                id = q.id,
                                category = c.name,
                                name = q.name,
                                part_no = q.pn,
                                supplier = string.Empty
                            };
            Expression<Func<ResponseGetMaterials, bool>> categoryExpression = q => true;
            if (!string.IsNullOrWhiteSpace(request.category))
            {
                categoryExpression = q => q.category.Contains(request.category);
            }
            Expression<Func<ResponseGetMaterials, bool>> nameExpression = q => true;
            if (!string.IsNullOrWhiteSpace(request.name))
            {
                nameExpression = q => q.name.Contains(request.name);
            }
            var predicates = categoryExpression.And(nameExpression);
            var dataList = queryList.Where(predicates).ToList();
            var resList = new List<ResponseGetMaterials>();
            var tooltechs = new List<siger_project_toollife_tech_tool>();
            if (!string.IsNullOrWhiteSpace(request.cutter_number) && product_id > 0 && route > 0)
            {
                var tech = _context.siger_project_toollife_technology.FirstOrDefault(t => t.product_id == product_id && t.route_id == route && t.project_id == projectId &&
                     t.status == (int)RowState.Valid);
                if (tech != null)
                {
                    tooltechs = _context.siger_project_toollife_tech_tool.Where(t =>
                       t.project_id == projectId && t.status == (int)RowState.Valid &&
                       t.cutter_number == request.cutter_number &&
                       t.technology_id == tech.id).ToList();
                }
            }
            foreach (var data in dataList)
            {
                var suppliers = from q in _context.siger_tr_material_supplier
                                join c in _context.siger_wms_bussinese_contacts on q.supplier_id equals c.id
                                where q.projectId == projectId && q.status == (int)RowState.Valid && c.status == (int)RowState.Valid
                                      && q.material_id == data.id
                                select new
                                {
                                    c.name
                                };
                data.supplier = string.Join(",", suppliers.Select(t => t.name).ToList());
                var techtool = tooltechs.FirstOrDefault(t => t.part_no == data.part_no);
                data.isselected = techtool == null ? 0 : 1;
                data.quantity = techtool?.quantity ?? 0;
                resList.Add(data);
            }
            return resList;
        }

        /// <summary>
        /// 根据物料获取物料供应商
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ResponseIdName> GetSuppliersByPartNo(int material_id, string partNo, int projectid)
        {
            var material = _context.siger_tr_materials.FirstOrDefault(t =>
                (t.pn == partNo || t.id == material_id && material_id > 0) && t.status == (int)RowState.Valid && t.projectId == projectid);
            if (material == null)
            {
                return new List<ResponseIdName>();
            }

            var suppliers = from q in _context.siger_tr_material_supplier
                            join c in _context.siger_wms_bussinese_contacts on q.supplier_id equals c.id
                            where q.projectId == projectid && q.status == (int)RowState.Valid && c.status == (int)RowState.Valid
                                  && q.material_id == material.id
                            select new ResponseIdName
                            {
                                id = q.supplier_id,
                                name = c.name
                            };
            return suppliers.ToList();
        }

        public IEnumerable<ResponseRoutes> GetRouteList(int productId, int projectid)
        {
            var query = Queryable.Where<siger_project_product_route>(_context.siger_project_product_route, t =>
                t.projectId == projectid && t.status == (int)RowState.Valid && t.productId == productId);
            var res = from q in query
                      select new ResponseRoutes
                      {
                          route = q.id,
                          text = q.name
                      };
            return res;
        }

        public siger_project_product_route GetProductRoute(int id, int projectid)
        {
            return Queryable.FirstOrDefault<siger_project_product_route>(_context.siger_project_product_route, t =>
                t.id == id && t.projectId == projectid && t.status == (int)RowState.Valid);
        }

        public IEnumerable<ResponseGetMaterials> GetMaterialList(int id, string part_no,int count, int projectid)
        {
            var queryList = from q in _context.siger_tr_materials
                            join c in _context.siger_tr_material_type on q.typeid equals c.id
                            where q.status == (int)RowState.Valid && q.projectId == projectid && c.status == (int)RowState.Valid
                            select new ResponseGetMaterials
                            {
                                id = q.id,
                                category = c.name,
                                name = q.name,
                                part_no = q.pn,
                                supplier = string.Empty,
                                spec = q.spec
                            };

            Expression<Func<ResponseGetMaterials, bool>> idExpression = q => true;
            if (id > 0)
            {
                idExpression = q => q.id == id;
            }
            Expression<Func<ResponseGetMaterials, bool>> pnExpression = q => true;
            if (!string.IsNullOrWhiteSpace(part_no))
            {
                pnExpression = q => q.part_no == part_no;
            }
            
            var expression = idExpression.And(pnExpression);
            if (count != 0)
            {
                return queryList.Where(expression).OrderByDescending(o=>o.id).Take(count).ToList();
            }
            return queryList.Where(expression).OrderByDescending(o => o.id).ToList();
        }

        public siger_tr_materials GetMaterial(int id, string part_no, int projectid)
        {
            return _context.siger_tr_materials.FirstOrDefault(t =>
                (t.pn == part_no || t.id == id && id > 0) && t.status == (int)RowState.Valid &&
                t.projectId == projectid);
        }

        public siger_project_user GetProjectUser(int id, int projectid)
        {
            return Queryable.FirstOrDefault<siger_project_user>(_context.siger_project_user, t =>
                t.mid == id && t.status == (int)RowState.Valid && t.projectid == projectid);
        }

        public siger_wms_stock GetStockTrace(string serial_number, int state, int projectid)
        {
            if (state > 0)
            {
                return _context.siger_wms_stock.FirstOrDefault(
                    t => (t.batch_number == serial_number || t.serial_number == serial_number) && t.projectid == projectid &&
                         t.status == (int)RowState.Valid && t.stock_state == state);
            }
            return _context.siger_wms_stock.FirstOrDefault(
                t => (t.batch_number == serial_number || t.serial_number == serial_number) && t.projectid == projectid &&
                     t.status == (int)RowState.Valid);
        }

        public IEnumerable<ResponseGetMaterialList> GetToolMaterialList(int id, string part_no, string name, int projectid)
        {
            var queryList = from q in _context.siger_tr_materials
                            join c in _context.siger_tr_material_type on q.typeid equals c.id
                            join s in _context.siger_project_toollife_tool on q.id equals s.material_id
                            where q.status == (int)RowState.Valid && q.projectId == projectid && c.status == (int)RowState.Valid
                                  && s.project_id == projectid && s.status == (int)RowState.Valid
                            select new ResponseGetMaterialList
                            {
                                id = q.id,
                                category = c.name,
                                name = q.name,
                                part_no = q.pn,
                                supplier = string.Empty,
                                grind = s.grind > 0 ? s.grind : 0
                            };

            Expression<Func<ResponseGetMaterialList, bool>> idExpression = q => true;
            if (id > 0)
            {
                idExpression = q => q.id == id;
            }
            Expression<Func<ResponseGetMaterialList, bool>> pnExpression = q => true;
            if (!string.IsNullOrWhiteSpace(part_no))
            {
                pnExpression = q => q.part_no.Contains(part_no);
            }
            Expression<Func<ResponseGetMaterialList, bool>> nameExpression = q => true;
            if (!string.IsNullOrWhiteSpace(name))
            {
                nameExpression = q => q.name.Contains(name);
            }
            var expression = idExpression.And(pnExpression).And(nameExpression);

            return queryList.Where(expression).ToList();
        }

        public siger_wms_bussinese_contacts GetSupplier(int id, int projectid)
        {
            return _context.siger_wms_bussinese_contacts.FirstOrDefault(t =>
                t.id == id && t.projectid == projectid && t.status == (int)RowState.Valid);
        }

        public IEnumerable<ResponseGetToolStockMaterials> GetStokcs(int material_id, int supplier_id, string serial_number, int projectid)
        {
            var query = _context.siger_wms_stock.Where(t =>
                t.material_id == material_id && t.businessid == supplier_id &&
                t.projectid == projectid && t.status == (int)RowState.Valid && t.stock_state == (int)StockEnum.InWavehouse);
            var queryList = from q in query
                            join m in _context.siger_tr_materials on q.material_id equals m.id
                            join sl in _context.siger_wms_storage_location on q.storage_location_id equals sl.id
                            join b in _context.siger_wms_bussinese_contacts on q.businessid equals b.id
                            select new ResponseGetToolStockMaterials
                            {
                                part_no = q.material_pn,
                                material_name = m.name ?? "",
                                material_id = q.material_id,
                                supplier_id = q.businessid,
                                quantity = q.quantity,
                                position = sl.name ?? "",
                                supplier = b.name ?? "",
                                collar_use = 1,
                                manage_mode = q.manage_model.ToInt(),
                                serial_number =
                                    q.manage_model.ToInt() == (int)managemodel.Batch ? q.batch_number : q.serial_number,
                                storage_id = sl.storageid > 0 ? sl.storageid : 0
                            };
            Expression<Func<ResponseGetToolStockMaterials, bool>> SnExpression = q => true;
            if (!string.IsNullOrWhiteSpace(serial_number))
            {
                SnExpression = q => q.serial_number == serial_number;
            }
            var stocks = new List<ResponseGetToolStockMaterials>();
            var list = queryList.Where(SnExpression).ToList();
            var stockList = queryList.Where(SnExpression).GroupBy(t => t.serial_number).Select(t => t.FirstOrDefault()).ToList();

            var SnList = stockList.Select(t => t.serial_number).ToList();
            foreach (var sn in SnList)
            {
                var stocklist = stockList.Where(t => t.serial_number == sn).GroupBy(t => t.position).Select(t => t.FirstOrDefault()).ToList();
                foreach (var stock in stocklist)
                {
                    var model = Mapper<ResponseGetToolStockMaterials, ResponseGetToolStockMaterials>.Map(stock);
                    model.quantity = list.Where(t => t.serial_number == stock.serial_number && t.position == stock.position).Sum(t => t.quantity);
                    if (model.quantity > 0)
                    {
                        stocks.Add(model);
                    }
                }
            }
            return stocks;
        }

        public IEnumerable<GetStockStorage> GetStockStorage(int material_id)
        {
            var query = from q in _context.siger_wms_stock
                        join s in _context.siger_wms_storage_location on q.storage_location_id equals s.id
                        join sl in _context.siger_wms_storage on s.storageid equals sl.id
                        where q.status == (int)RowState.Valid && sl.status == (int)RowState.Valid && s.status == (int)RowState.Valid
                        && q.material_id == material_id
                        select new GetStockStorage
                        {
                            stock_id = q.id,
                            supplier_id = q.businessid,
                            storage_id = s.storageid,
                            storage_location_id = s.id,
                            material_id = q.material_id,
                            material_pn = q.material_pn
                        };
            return query;
        }

        public siger_project_product GetProduct(int productId, int projectid)
        {
            return Queryable.SingleOrDefault<siger_project_product>(_context.siger_project_product, t => t.id == productId && t.projectid == projectid && t.status == (int)RowState.Valid);
        }

        public siger_wms_stock GetSerialNoStock(string serial_number, int state, int projectid)
        {
            if (state > 0)
            {
                return _context.siger_wms_stock.FirstOrDefault(
                    t => t.serial_number == serial_number && t.projectid == projectid &&
                         t.status == (int)RowState.Valid && t.stock_state == state);
            }
            return _context.siger_wms_stock.FirstOrDefault(
                t => t.serial_number == serial_number && t.projectid == projectid &&
                     t.status == (int)RowState.Valid);
        }
        protected IEnumerable<siger_project_level_section> GetSonLeveSection(IEnumerable<siger_project_level_section>sections,int parentId)
        {
            var query = sections.Where(f => f.parentid == parentId);
            return query.ToList().Concat(query.SelectMany(s => GetSonLeveSection(sections, s.id)));
        }
        public List<siger_project_level_section> GetSonLeveSection(int parentId,int projectId)
        {
            var ret = new List<siger_project_level_section>();
            var list = _context.siger_project_level_section.Where(f => f.projectid == projectId).AsNoTracking().ToList();
            var parent = list.FirstOrDefault(f => f.id == parentId);
            if (parent!=null)
            {
                ret = GetSonLeveSection(list, parentId).ToList();
                ret.Add(parent);
            }
            else
            {
                ret = list.ToList();
            }
            return ret.Distinct().ToList();
        }
        public List<int> GetMachineIds(int section, int projectId)
        {
            var ret = new List<int>();
            var sections = GetSonLeveSection(section, projectId).Select(s => s.id);
            ret = _context.siger_project_machine_attribution.Where(f => sections.Contains(f.station) && f.projectid == projectId && f.status != 0).AsNoTracking().Select(s => s.machine).ToList();
            return ret;
        }

        public List<siger_project_machine> GetMachineInfo(int section, int projectId)
        {
            var mids = GetMachineIds(section, projectId);
            return _context.siger_project_machine.Where(f => f.projectid == projectId && f.status != 0 && mids.Contains(f.id)).AsNoTracking().ToList();
        }
    }
}
