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.Helpers;
using Siger.Middlelayer.Repository.Data;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Repository.Extensions;
using Siger.Middlelayer.Repository.Paged;
using Siger.Middlelayer.Repository.Repositories.Interface;
using Siger.Middlelayer.Repository.Response;
using Siger.Middlelayer.Utility.ImportEntities;

namespace Siger.Middlelayer.Repository.Repositories
{
    internal class SigerTrMetrialsRepository : ApiConfigRepositoryBase<siger_tr_materials>, ISigerTrMaterialsRepository
    {
        private readonly ApiConfigDbContext materialsContext;
        public SigerTrMetrialsRepository(ApiConfigDbContext context) : base(context)
        {
            materialsContext = context;
        }

        public IPagedCollectionResult<Material> GetPagedListEx(string name, int exp, string pn, int pid, int projectid, int page, int pagesize)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int)LanguageType.EN);
            var yes = transHelper.GetTranslateText(TranslateEnEnum.Yes);
            var no = transHelper.GetTranslateText(TranslateEnEnum.No);
            int rowstate = (int)RowState.Valid;
            var materials =
                materialsContext.siger_tr_materials.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid);
            var query = from stms in materials
                        join q in materialsContext.siger_tr_material_type
                        on new { typeids = stms.typeid, projectids = projectid, rowstatus = rowstate }
                        equals new { typeids = q.id, projectids = q.projectId, rowstatus = q.status }
                        into temp
                        from q in temp.DefaultIfEmpty()
                        select new Material
                        {
                            id = stms.id,
                            name = string.IsNullOrEmpty(stms.name) ? "" : stms.name,
                            typeid = stms.typeid,
                            typeid_value = q.name,
                            spec = string.IsNullOrEmpty(stms.spec) ? "" : stms.spec,
                            pn = string.IsNullOrEmpty(stms.pn) ? "" : stms.pn,
                            supplier = string.IsNullOrEmpty(stms.supplier) ? "" : stms.supplier,
                            manage_mode = stms.manage_mode,
                            unit = stms.unit,
                            conv_unit = stms.conv_unit,
                            conv_proportion = stms.conv_proportion ?? "",
                            price = stms.price,
                            min_stock = stms.min_stock,
                            max_stock = stms.max_stock,
                            is_disable = stms.is_disable,
                            is_disable_value = stms.is_disable == 1 ? yes : no,
                            is_first = stms.is_first,
                            is_first_value = stms.is_first == 1 ? yes : no,
                            priority = stms.priority,
                            exp = stms.exp > 0 ? stms.exp.ToString() : "",
                            desc = stms.desc,
                            image = stms.image,
                            category = stms.category
                        };
            Expression<Func<Material, bool>> nameExpression = q => true;
            if (!string.IsNullOrEmpty(name))
            {
                nameExpression = q => q.name.Contains(name);
            }
            Expression<Func<Material, bool>> expExpression = q => true;
            if (exp > 0)
            {
                expExpression = q => q.exp.ToInt() >= exp;
            }
            Expression<Func<Material, bool>> pidExpression = q => true;
            if (pid > 0)
            {
                pidExpression = q => q.typeid == pid;
            }
            Expression<Func<Material, bool>> pnExpression = q => true;
            if (!string.IsNullOrWhiteSpace(pn))
            {
                pnExpression = q => q.pn.Equals(pn);
            }
            var predicate = nameExpression.And(expExpression).And(pidExpression).And(pnExpression);
            var totalCount = query.Count(predicate);
            var entities = query.Where(predicate).OrderByDescending(q => q.id).Skip((page - 1) * pagesize).Take(pagesize).AsNoTracking().ToList();
            foreach (var item in entities)
            {
                item.supplier_value = GetDvalInDict(projectid, AccDictCost.suppliertype, item.supplier);
                item.manage_mode_value = GetDvalInDict(0, AccDictCostNoProjectId.managemodel, item.manage_mode);
                item.unit_value = GetDvalInDict(projectid, AccDictCost.meterunits, item.unit);
                item.conv_unit_value = GetDvalInDict(projectid, AccDictCost.convunits, item.conv_unit);
                item.priority_value = GetDvalInDict(projectid, AccDictCost.prioritytype, item.priority);
            }
            return new PagedCollectionResult<Material>(entities, totalCount);
        }

        public IEnumerable<Material> GetMaterialList(string name, int exp, int parentid, int projectid)
        {
            var materials =
                materialsContext.siger_tr_materials.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid);
            var query = from stms in materials
                        join q in materialsContext.siger_tr_material_type on stms.typeid equals q.id
                        into temp
                        from q in temp.DefaultIfEmpty()
                        where q.projectId == projectid && q.status == (int)RowState.Valid
                        select new Material
                        {
                            id = stms.id,
                            typeid = q.id > 0 ? q.id : 0,
                            name = stms.name,
                            typeid_value = q.name,
                            spec = stms.spec,
                            pn = stms.pn,
                            supplier = stms.supplier,
                            manage_mode = stms.manage_mode,
                            unit = stms.unit,
                            conv_unit = stms.conv_unit,
                            conv_proportion = stms.conv_proportion,
                            price = stms.price,
                            min_stock = stms.min_stock,
                            max_stock = stms.max_stock,
                            is_disable = stms.is_disable,
                            is_first = stms.is_first,
                            priority = stms.priority,
                            exp = stms.exp > 0 ? stms.exp.ToString() : "",
                            desc = stms.desc,
                            image = stms.image
                        };
            Expression<Func<Material, bool>> nameExpression = q => true;
            if (!string.IsNullOrEmpty(name))
            {
                nameExpression = q => q.name.Contains(name);
            }
            Expression<Func<Material, bool>> expExpression = q => true;
            if (exp > 0)
            {
                expExpression = q => q.exp.ToInt() >= exp;
            }
            Expression<Func<Material, bool>> parentidExpression = q => true;
            if (parentid > 0)
            {
                parentidExpression = q => q.typeid == parentid;
            }
            var predicate = nameExpression.And(expExpression).And(parentidExpression);

            var entities = query.Where(predicate).OrderByDescending(q => q.id).AsNoTracking().ToList();

            return entities;
        }

        public CommonImportResult ImportMaterials(IEnumerable<MaterialTemplate> materials, int projectid)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int)LanguageType.EN);
            var yes = transHelper.GetTranslateText(TranslateEnEnum.Yes);
            var materialLists = materials.ToList();
            var entitis = new List<siger_tr_materials>();

            var errors = new List<string>();
            var rowIndex = 1;
            foreach (var material in materialLists)
            {
                rowIndex++;

                var is_first = (material.is_first == yes) ? 1 : 0;
                var entity = materialsContext.siger_tr_materials.FirstOrDefault(q => (q.pn == material.pn || q.name == material.name && q.spec == material.spec)
                    && q.projectId == projectid && q.status == (int)RowState.Valid);
                var typeEntity = materialsContext.siger_tr_material_type.FirstOrDefault(q => q.name == material.typename && q.status == (int)RowState.Valid && q.projectId == projectid);
                var uintentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.unit && t.status == (int)RowState.Valid && t.cat == AccDictCost.meterunits && t.projectId == projectid);
                var priorityentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.priority && t.status == (int)RowState.Valid && t.cat == AccDictCost.prioritytype && t.projectId == projectid);
                var manage_modeentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.manage_mode && t.status == (int)RowState.Valid && t.cat == AccDictCostNoProjectId.managemodel);
                var conv_unitentity = !string.IsNullOrEmpty(material.conv_unit) ? materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.conv_unit
                    && t.status == (int)RowState.Valid && t.cat == AccDictCost.convunits && t.projectId == projectid) : new SigerTrDict();

                var repeatmodel = materialLists.Where(q => (q.pn == material.pn || q.name == material.name && q.spec == material.spec));
                if (entity != null || repeatmodel.Any() && repeatmodel.Count() > 1)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.DataExist}");
                }
                if (typeEntity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.MaterislTypeIsEmpty}");
                }
                if (uintentity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.UnitNotFound}");
                }
                if (is_first == 1 && priorityentity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.PriorityIsEmpty}");
                }
                if (manage_modeentity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.ErrorManageModel}");
                }
                if (conv_unitentity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.ConvertUnitError}");
                }

                var min_stock = material.min_stock.ToInt();
                var max_stock = material.max_stock.ToInt();
                if (min_stock < 0 || max_stock < 0)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.StcokNotLessThanZero}");
                }
                if ((min_stock > 0 || max_stock > 0) && min_stock > max_stock)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.MaxMinStockError}");
                }
            }

            if (errors.Any())
            {
                return new CommonImportResult(0, string.Join(";", errors));
            }

            foreach (var material in materialLists)
            {
                var is_disable = (material.is_disable == yes) ? 1 : 0;
                var is_first = (material.is_first == yes) ? 1 : 0;

                var typeEntity = materialsContext.siger_tr_material_type.FirstOrDefault(q => q.name == material.typename && q.status == (int)RowState.Valid && q.projectId == projectid);

                var uintentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.unit && t.status == (int)RowState.Valid && t.cat == AccDictCost.meterunits && t.projectId == projectid);
                var priorityentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.priority && t.status == (int)RowState.Valid && t.cat == AccDictCost.prioritytype && t.projectId == projectid);
                var manage_modeentity = materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.manage_mode && t.status == (int)RowState.Valid && t.cat == AccDictCostNoProjectId.managemodel);
                var conv_unitentity = !string.IsNullOrEmpty(material.conv_unit) ? materialsContext.siger_tr_dict.FirstOrDefault(t => t.dval == material.conv_unit
                    && t.status == (int)RowState.Valid && t.cat == AccDictCost.convunits && t.projectId == projectid) : new SigerTrDict();

                var materialEntity = new siger_tr_materials
                {
                    typeid = typeEntity.id,
                    name = material.name ?? "",
                    spec = material.spec ?? "",
                    pn = material.pn ?? "",
                    supplier = material.supplier,
                    manage_mode = manage_modeentity.dkey ?? "",
                    unit = uintentity.dkey ?? "",
                    conv_proportion = material.conv_proportion ?? "",
                    conv_unit = conv_unitentity.dkey ?? "",
                    price = material.price.ToDouble(),
                    min_stock = material.min_stock.ToInt(),
                    max_stock = material.max_stock.ToInt(),
                    is_disable = is_disable,
                    is_first = is_first,
                    priority = priorityentity != null ? priorityentity.dkey : "",
                    exp = material.exp.ToInt(),
                    desc = material.desc ?? "",
                    image = material.image ?? "",
                    projectId = projectid,
                    status = (int)RowState.Valid,
                    transdatetime = DateTime.Now
                };
                entitis.Add(materialEntity);
            }
            try
            {
                foreach (var entity in entitis)
                {
                    var suppliers = new List<int>();
                    var supplierNames = entity.supplier.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    var supplierList = new List<SigerTrMaterialSupplier>();
                    foreach (var name in supplierNames)
                    {
                        var company = materialsContext.siger_wms_bussinese_contacts.FirstOrDefault(t =>
                            (t.name == name || t.enterprise_name == name) && t.status == (int)RowState.Valid && t.projectid == projectid);
                        if (company != null)
                        {
                            var model = new SigerTrMaterialSupplier
                            {
                                material_id = entity.id,
                                projectId = projectid,
                                status = (int)RowState.Valid,
                                supplier_id = company.id
                            };
                            supplierList.Add(model);
                            suppliers.Add(company.id);
                        }
                    }
                    entity.supplier = string.Join(",", suppliers);
                    materialsContext.siger_tr_materials.Add(entity);
                    if (materialsContext.SaveChanges() > 0)
                    {
                        foreach (var supplier in supplierList)
                        {
                            supplier.material_id = entity.id;
                        }
                        materialsContext.siger_tr_material_supplier.AddRange(supplierList);
                        materialsContext.SaveChanges();
                    }
                }
                return new CommonImportResult(1, "1");
            }
            catch
            {
                throw;
            }
        }

        public int GetMaxId()
        {
            if (materialsContext.siger_tr_materials.Count() > 0)
            {
                return materialsContext.siger_tr_materials.Max(t => t.id);
            }
            return 0;
        }

        public int InsertSupplier(SigerTrMaterialSupplier supplier)
        {
            materialsContext.siger_tr_material_supplier.Add(supplier);
            return materialsContext.SaveChanges();
        }

        public void UpdateSupplier(SigerTrMaterialSupplier supplier)
        {
            materialsContext.siger_tr_material_supplier.Update(supplier);
        }

        public void DeleteSuppliers(int materialId, int projectId)
        {
            var suppliers = materialsContext.siger_tr_material_supplier.Where(t =>
                t.projectId == projectId && t.material_id == materialId && t.status == (int)RowState.Valid);
            foreach (var supplier in suppliers)
            {
                supplier.status = (int)RowState.Invalid;
                materialsContext.Update(supplier);
            }
        }

        public int InsertMaterial(siger_tr_materials material)
        {
            materialsContext.siger_tr_materials.Add(material);
            return materialsContext.SaveChanges();
        }

        public int UpdateMaterial(siger_tr_materials material)
        {
            materialsContext.siger_tr_materials.Update(material);
            return materialsContext.SaveChanges();
        }

        private string GetDvalInDict(int pid, string cat, string dkey)
        {
            var dval = materialsContext.siger_tr_dict.Where(f =>
                     (f.projectId == pid || f.projectId == 0) && f.status == (int)RowState.Valid && f.cat.Equals(cat) &&
                     f.dkey.Equals(dkey))
                .Select(s => s.dval).FirstOrDefault();
            return dval ?? "";
        }

        public PagedCollectionResult<Material> GetMaterialsData(string name, int exp, string pn, int typeid, int supplierId, int pid, int page, int pagesize)
        {
            Expression<Func<Material, bool>> funcName = f => true;
            Expression<Func<Material, bool>> funcExp = f => true;
            Expression<Func<Material, bool>> funcPn = f => true;
            Expression<Func<Material, bool>> funcTypeid = f => true;
            var rowstate = (int)RowState.Valid;
            var transHelper = new TranslateHelper(GetProjectLanguage(pid) == (int)LanguageType.EN);
            var yes = transHelper.GetTranslateText(TranslateEnEnum.Yes);
            var no = transHelper.GetTranslateText(TranslateEnEnum.No);
            var materials =
               materialsContext.siger_tr_materials.Where(q => q.status == (int)RowState.Valid && q.projectId == pid);
            var query = from stms in materials
                        join q in materialsContext.siger_tr_material_type
                        on new { typeids = stms.typeid, projectids = pid, rowstatus = rowstate }
                        equals new { typeids = q.id, projectids = q.projectId, rowstatus = q.status }
                        into temp
                        from q in temp.DefaultIfEmpty()
                        select new Material
                        {
                            id = stms.id,
                            name = string.IsNullOrEmpty(stms.name) ? "" : stms.name,
                            typeid = stms.typeid,
                            typeid_value = q.name,
                            spec = string.IsNullOrEmpty(stms.spec) ? "" : stms.spec,
                            pn = string.IsNullOrEmpty(stms.pn) ? "" : stms.pn,
                            supplier = string.IsNullOrEmpty(stms.supplier) ? "" : stms.supplier,
                            manage_mode = stms.manage_mode,
                            unit = stms.unit,
                            conv_unit = stms.conv_unit,
                            conv_proportion = stms.conv_proportion ?? "",
                            price = stms.price,
                            min_stock = stms.min_stock,
                            max_stock = stms.max_stock,
                            is_disable = stms.is_disable,
                            is_disable_value = stms.is_disable == 1 ? yes : no,
                            is_first = stms.is_first,
                            is_first_value = stms.is_first == 1 ? yes : no,
                            priority = stms.priority,
                            exp = stms.exp > 0 ? stms.exp.ToString() : "",
                            desc = stms.desc,
                            image = stms.image
                        };
            if (!string.IsNullOrEmpty(name))
            {
                funcName = f => f.name.Contains(name);
            }
            if (!string.IsNullOrEmpty(pn))
            {
                funcPn = f => f.pn.Contains(pn);
            }
            if (exp != 0)
            {
                funcExp = f => f.exp.ToInt() == exp;
            }
            if (typeid != 0)
            {
                funcTypeid = f => f.typeid == typeid;
            }
            Expression<Func<Material, bool>> supplierIdFunc = f => true;
            if (supplierId != 0)
            {
                supplierIdFunc = f => f.supplier.Contains(supplierId.ToStr());
            }
            var predicate = funcName.And(funcPn).And(funcExp).And(funcTypeid).And(supplierIdFunc);
            var totalCount = query.Count(predicate);
            List<Material> entities;
            //开启分页
            if (page != 0)
            {
                entities = query.Where(predicate).OrderByDescending(q => q.id).Skip((page - 1) * pagesize).Take(pagesize).AsNoTracking().ToList();
            }
            else
            {
                entities = query.Where(predicate).OrderByDescending(q => q.id).Take(pagesize).AsNoTracking().ToList();
            }
            foreach (var item in entities)
            {
                item.supplierdata = GetSupplierData(item.supplier);
                item.manage_mode_value = GetDvalInDict(pid, AccDictCostNoProjectId.managemodel, item.manage_mode);
                item.unit_value = GetDvalInDict(pid, AccDictCost.meterunits, item.unit);
                item.conv_unit_value = GetDvalInDict(pid, AccDictCost.convunits, item.conv_unit);
                item.priority_value = GetDvalInDict(pid, AccDictCost.prioritytype, item.priority);
            }
            return new PagedCollectionResult<Material>(entities, totalCount);
        }

        public List<siger_wms_bussinese_contacts> GetSupplierData(string supplierId)
        {
            var resp = new List<siger_wms_bussinese_contacts>();
            var suppliers = supplierId.Split(',');
            foreach (var supplier in suppliers)
            {
                var supplierData = materialsContext.siger_wms_bussinese_contacts.FirstOrDefault(f => f.id == supplier.ToInt() && f.status == (int)RowState.Valid);
                if (supplierData != null)
                {
                    resp.Add(supplierData);
                }
            }
            return resp;
        }

        public IEnumerable<ResponseTrMaterial> GetTrMaterialsByProductId(int projectId, int parentId)
        {
            var query = from ps in materialsContext.siger_project_product_structure
                        join m in materialsContext.siger_tr_materials on ps.itemid equals m.id
                        where ps.projectid == projectId && ps.itemtype == Common.ModuleEnum.ComboxItem.Material 
                        && ps.status == (int)RowState.Valid
                        && ps.parent_id==parentId
                        select new ResponseTrMaterial
                        {
                            id = m.id,
                            name = m.name,
                            pn = m.pn,
                            spec = m.spec
                        };

            return query.AsEnumerable().GroupBy(g=>g.id).Select(s=>s.First());
        }

        public IEnumerable<ResponseSonMaterials> GetSonMaterialsByProductId(int projectId, int parentId)
        {
            var query = from ps in materialsContext.siger_project_product_structure
                        join m in materialsContext.siger_tr_materials on ps.itemid equals m.id
                        where ps.projectid == projectId && ps.itemtype == Common.ModuleEnum.ComboxItem.Material
                        && ps.status == (int)RowState.Valid
                        && ps.parent_id == parentId
                        select new ResponseSonMaterials
                        {
                            structureid = ps.id,
                            id = m.id,
                            name = m.name,
                            pn = m.pn,
                            spec = m.spec
                        };
            var list = new List<ResponseSonMaterials>();
            foreach(var q in query.ToList())
            {
                list.Add(q);
                var sons = GetSonMaterialsByParentId(q.structureid, projectId).ToList();
                list.AddRange(sons);
            }

            return list;
        }

        private IEnumerable<ResponseSonMaterials> GetSonMaterialsByParentId(int parentId, int projectId)
        {
            var querylist = materialsContext.siger_project_product_structure.Where(t => t.parent_id == parentId && 
                t.status == (int)RowState.Valid && t.projectid == projectId && t.itemtype == Common.ModuleEnum.ComboxItem.Material);
            var query = from q in querylist
                        join m in materialsContext.siger_tr_materials on q.itemid equals m.id
                        select new ResponseSonMaterials
                        {
                            structureid = q.id,
                            id = m.id,
                            name = m.name,
                            pn = m.pn,
                            spec = m.spec
                        };

            return query.ToList().Concat(query.ToList().SelectMany(t => GetSonMaterialsByParentId(t.structureid, projectId))).ToList();
        }

        public IEnumerable<ResponseIdName> GetSuppliersByMaterialId(int materialid, int projectId)
        {
            var query = from q in materialsContext.siger_tr_material_supplier
                           join s in materialsContext.siger_wms_bussinese_contacts on q.supplier_id equals s.id
                           where q.material_id == materialid && q.status == (int)RowState.Valid && q.projectId == projectId
                           && s.projectid == projectId && s.status == (int)RowState.Valid
                           select new ResponseIdName
                           {
                               id = s.id,
                               name = s.name
                           };
            return query;
        }

        public IEnumerable<ResponseMaterialSupplier> GetMaterialSuppliers(int projectId)
        {
            var query = from q in materialsContext.siger_tr_materials
                join s in materialsContext.siger_wms_bussinese_contacts on q.supplier equals s.id.ToString() into temp
                from ss in temp.DefaultIfEmpty()
                where q.status == (int) RowState.Valid && q.projectId == projectId
                select new ResponseMaterialSupplier
                {
                    id = q.id,
                    name = q.name,
                    pn = q.pn,
                    spec = q.spec,
                    supplier = ss != null ? ss.enterprise_name : ""
                };
            return query;
        }
    }
}
