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.Utility.ImportEntities;

namespace Siger.Middlelayer.Repository.Repositories
{
    internal class SigerTrProductStructureRepository : ApiConfigRepositoryBase<SigerTrProductStructure>, ISigerTrProductStructureRepository
    {
        private ApiConfigDbContext accDbContext;
        public SigerTrProductStructureRepository(ApiConfigDbContext context) : base(context)
        {
            accDbContext = context;
        }

        public IPagedCollectionResult<ProductStructure> GetPagedList(string name, string pn, int pid, int projectid, int page = 1, int pagesize = 10)
        {
            var productStructures =
                accDbContext.siger_tr_product_structure.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid);
            var query = from p in productStructures
                        join m in accDbContext.siger_tr_materials
                        on p.pn equals m.pn
                        into mtemp
                        from m in mtemp.DefaultIfEmpty()
                        join q in accDbContext.siger_tr_material_type
                        on m.typeid equals q.id
                        into temp
                        from q in temp.DefaultIfEmpty()
                        join d in accDbContext.siger_tr_dict
                        on new { dkeys = p.unit_id, dcat = AccDictCost.meterunits, rowstatus = (int)RowState.Valid, projectids = projectid }
                        equals new { dkeys = d.dkey, dcat = d.cat, rowstatus = d.status, projectids = d.projectId }
                        into dtemp
                        from d in dtemp.DefaultIfEmpty()
                        where p.parentid == 0 && m.projectId == projectid && m.status == (int)RowState.Valid
                        select new ProductStructure
                        {
                            id = p.id,
                            serial_number = p.serial_number,
                            name = m.name ?? "",
                            spec = m.spec ?? "",
                            pn = p.pn ?? "",
                            material_type_id = m.typeid > 0 ? m.typeid : 0,
                            material_type_id_value = q.name ?? "",
                            unit_id = p.unit_id,
                            unit_id_value = d.dval ?? "",
                            desc = p.desc,
                            quantity = p.quantity,
                            transdatetime = UnixTimeHelper.ConvertIntDateTime(p.TransDateTime)
                        };

            var query1 = from p in productStructures
                         join m in accDbContext.siger_tr_materials
                         on p.pn equals m.pn
                         into mtemp
                         from m in mtemp.DefaultIfEmpty()
                         join q in accDbContext.siger_tr_material_type
                         on m.typeid equals q.id
                         into temp
                         from q in temp.DefaultIfEmpty()
                         join d in accDbContext.siger_tr_dict
                        on new { dkeys = p.unit_id, dcat = AccDictCost.meterunits, rowstatus = (int)RowState.Valid, projectids = projectid }
                        equals new { dkeys = d.dkey, dcat = d.cat, rowstatus = d.status, projectids = d.projectId }
                        into dtemp
                         from d in dtemp.DefaultIfEmpty()
                         where p.parentid != 0 && m.projectId == projectid && m.status == (int)RowState.Valid
                         select new ProductStructure
                         {
                             id = p.id,
                             pid = p.parentid,
                             serial_number = p.serial_number,
                             name = m.name ?? "",
                             spec = m.spec ?? "",
                             pn = p.pn,
                             material_type_id = m.typeid > 0 ? m.typeid : 0,
                             material_type_id_value = q.name ?? "",
                             unit_id = p.unit_id ?? "",
                             unit_id_value = d.dval ?? "",
                             desc = p.desc,
                             quantity = p.quantity,
                             transdatetime = UnixTimeHelper.ConvertIntDateTime(p.TransDateTime)
                         };


            Expression<Func<ProductStructure, bool>> pidExpression = q => true;
            if (pid > 0)
            {
                var ids = GetStructureTypeIds(pid, projectid);
                ids.Add(pid);
                pidExpression = q => ids.Contains(q.id);
            }
            Expression<Func<ProductStructure, bool>> nameExpression = q => true;
            if (!string.IsNullOrEmpty(name))
            {
                nameExpression = q => q.name.Contains(name);
            }
            Expression<Func<ProductStructure, bool>> pnExpression = q => true;
            if (!string.IsNullOrEmpty(pn))
            {
                pnExpression = q => q.pn.Contains(pn);
            }

            var predicate = nameExpression.And(pnExpression).And(pidExpression);

            //var querylist = query.Union(query1);
            var a = query1.ToList();
            var querylist = new List<ProductStructure>();
            foreach(var item in query)
            {
                var resultlist = new List<ProductStructure>();
                querylist.Add(item);
                GetTreeSonList(query1.ToList(), item.id, resultlist);
                querylist.AddRange(resultlist);
            }

            var querys = querylist.AsQueryable();

            var totalCount = querys.Count(predicate);
            var entities = querys.Where(predicate).Skip((page - 1) * pagesize).Take(pagesize).AsNoTracking().ToList();
            return new PagedCollectionResult<ProductStructure>(entities, totalCount);
        }

        public List<ProductStructure> GetListByParentId(int pid, int projectid)
        {
            var productStructures =
                accDbContext.siger_tr_product_structure.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid);
            var query = from p in productStructures
                        join m in accDbContext.siger_tr_materials
                        on p.pn equals m.pn
                        into mtemp
                        from m in mtemp.DefaultIfEmpty()
                        join q in accDbContext.siger_tr_material_type
                        on m.typeid equals q.id
                        into temp
                        from q in temp.DefaultIfEmpty()
                        join d in accDbContext.siger_tr_dict
                        on new { dkeys = p.unit_id, dcat = AccDictCost.meterunits, rowstatus = (int)RowState.Valid, projectids = projectid }
                        equals new { dkeys = d.dkey, dcat = d.cat, rowstatus = d.status, projectids = d.projectId }
                        into dtemp
                        from d in dtemp.DefaultIfEmpty()
                        where m.projectId == projectid && m.status == (int)RowState.Valid
                        select new ProductStructure
                        {
                            id = p.id,
                            pid = p.parentid,
                            material_id = p.material_id > 0 ? p.material_id : 0,
                            serial_number = p.serial_number,
                            name = m.name ?? "",
                            spec = m.spec ?? "",
                            pn = p.pn,
                            material_type_id = m.typeid > 0 ? m.typeid : 0,
                            material_type_id_value = q.name ?? "",
                            unit_id = p.unit_id ?? "",
                            unit_id_value = d.dval ?? "",
                            desc = p.desc,
                            quantity = p.quantity,
                            transdatetime = UnixTimeHelper.ConvertIntDateTime(p.TransDateTime)
                        };

            var resultlist = new List<ProductStructure>();
            resultlist.Add(query.FirstOrDefault(t=>t.id==pid));
            GetTreeSonList(query.ToList(), pid, resultlist);

            return resultlist;
        }

        private void GetTreeSonList(List<ProductStructure> models, int parentid, List<ProductStructure> resultList)
        {
            var modelList = models.FindAll(t => t.pid == parentid);
            if(modelList != null && modelList.Any())
            {
                modelList = modelList.OrderBy(t => t.serial_number.Split('.').Last() != null ? t.serial_number.Split('.').Last().ToInt() : 0).ToList();
                foreach (var item in modelList)
                {
                    parentid = item.id;
                    resultList.Add(item);
                    GetTreeSonList(models, parentid, resultList);
                }
            }
        }

        public IList<int> GetStructureTypeIds(int id, int projectid)
        {
            var list = new List<int>();
            var query = GetSonStructureTypes(id, projectid);

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

            return list;
        }

        private IEnumerable<SigerTrProductStructure> GetSonStructureTypes(int parentId, int projectid)
        {
            var query = from c in accDbContext.siger_tr_product_structure
                        where c.parentid == parentId && c.projectId == projectid && c.status == (int)RowState.Valid
                        select c;

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

        public List<ProductStructure> GetDataList(string name, string pn, int pid, int projectid)
        {
            var productStructures =
                accDbContext.siger_tr_product_structure.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid);
            var query = from p in productStructures
                        join m in accDbContext.siger_tr_materials
                        on p.pn equals m.pn
                        into mtemp
                        from m in mtemp.DefaultIfEmpty()
                        join q in accDbContext.siger_tr_material_type
                        on m.typeid equals q.id
                        into temp
                        from q in temp.DefaultIfEmpty()
                        join d in accDbContext.siger_tr_dict
                        on new { dkeys = p.unit_id, dcat = AccDictCost.meterunits, rowstatus = (int)RowState.Valid, projectids = projectid }
                        equals new { dkeys = d.dkey, dcat = d.cat, rowstatus = d.status, projectids = d.projectId }
                        into dtemp
                        from d in dtemp.DefaultIfEmpty()
                        where p.parentid == 0 && m.projectId == projectid && m.status == (int)RowState.Valid
                        select new ProductStructure
                        {
                            id = p.id,
                            serial_number = p.serial_number,
                            name = m.name ?? "",
                            spec = m.spec ?? "",
                            pn = p.pn,
                            material_type_id = m.typeid > 0 ? m.typeid : 0,
                            material_type_id_value = q.name ?? "",
                            unit_id = p.unit_id ?? "",
                            unit_id_value = d.dval ?? "",
                            desc = p.desc,
                            quantity = p.quantity,
                            transdatetime = UnixTimeHelper.ConvertIntDateTime(p.TransDateTime)
                        };

            var query1 = from p in productStructures
                         join m in accDbContext.siger_tr_materials
                         on p.pn equals m.pn
                         into mtemp
                         from m in mtemp.DefaultIfEmpty()
                         join q in accDbContext.siger_tr_material_type
                         on m.typeid equals q.id
                         into temp
                         from q in temp.DefaultIfEmpty()
                         join d in accDbContext.siger_tr_dict
                         on new { dkeys = p.unit_id, dcat = AccDictCost.meterunits, rowstatus = (int)RowState.Valid, projectids = projectid }
                         equals new { dkeys = d.dkey, dcat = d.cat, rowstatus = d.status, projectids = d.projectId }
                         into dtemp
                         from d in dtemp.DefaultIfEmpty()
                         join pi in accDbContext.siger_tr_product_structure
                         on new { pid = p.parentid, rowstatus = (int)RowState.Valid }
                         equals new { pid = pi.id, rowstatus = pi.status }
                         into pitemp
                         from pi in pitemp.DefaultIfEmpty()
                         where p.parentid != 0 && m.projectId == projectid && m.status == (int)RowState.Valid
                         select new ProductStructure
                         {
                             id = p.id,
                             pid = p.parentid,
                             serial_number = p.serial_number,
                             name = m.name ?? "",
                             spec = m.spec ?? "",
                             pn = p.pn,
                             material_type_id = m.typeid > 0 ? m.typeid : 0,
                             material_type_id_value = q.name ?? "",
                             unit_id = p.unit_id ?? "",
                             unit_id_value = d.dval ?? "",
                             desc = p.desc,
                             quantity = p.quantity,
                             transdatetime = UnixTimeHelper.ConvertIntDateTime(p.TransDateTime),
                             parent_pn = pi.pn ?? ""
                         };


            Expression<Func<ProductStructure, bool>> pidExpression = q => true;
            if (pid > 0)
            {
                var ids = GetStructureTypeIds(pid, projectid);
                ids.Add(pid);
                pidExpression = q => ids.Contains(q.id);
            }
            Expression<Func<ProductStructure, bool>> nameExpression = q => true;
            if (!string.IsNullOrEmpty(name))
            {
                nameExpression = q => q.name.Contains(name);
            }
            Expression<Func<ProductStructure, bool>> pnExpression = q => true;
            if (!string.IsNullOrEmpty(pn))
            {
                pnExpression = q => q.pn.Contains(pn);
            }

            var predicate = nameExpression.And(pnExpression).And(pidExpression);

            //var querylist = query.Union(query1);
            var a = query1.ToList();
            var querylist = new List<ProductStructure>();
            foreach (var item in query)
            {
                var resultlist = new List<ProductStructure>();
                querylist.Add(item);
                GetTreeSonList(query1.ToList(), item.id, resultlist);
                querylist.AddRange(resultlist);
            }

            var querys = querylist.AsQueryable();

            var entities = querys.Where(predicate).AsNoTracking().ToList();
            return entities;
        }

        public CommonImportResult ImportPorductStructure(IEnumerable<ProductStructureTemplate> productStructure, int projectid)
        {
            var entities = new List<SigerTrProductStructure>();
            var ProductStructureLists = productStructure.ToList();
            var add_data = new List<ProductStructureTemplate>();

            var errors = new List<string>();
            var rowIndex = 1;
            foreach (var p in ProductStructureLists)
            {
                rowIndex++;
                var entity = accDbContext.siger_tr_materials.FirstOrDefault(t => t.pn == p.pn && t.status == (int)RowState.Valid && t.projectId == projectid);
                var checkmodel = accDbContext.siger_tr_product_structure.FirstOrDefault(t => t.pn == p.pn  && t.projectId == projectid && t.status == (int)RowState.Valid);
                var model = accDbContext.siger_tr_materials.FirstOrDefault(t => t.pn == p.parent_pn && t.projectId == projectid && t.status == (int)RowState.Valid);
                var checkp_pn = accDbContext.siger_tr_product_structure.FirstOrDefault(t => t.pn == p.parent_pn && t.projectId == projectid && t.status == (int)RowState.Valid);
                var checkp_pn1 = add_data.FirstOrDefault(t => t.pn == p.parent_pn);

                if (p.pn == p.parent_pn)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.ParentPnNotEqualSonPn}");
                }
                if (model == null)
                    errors.Add($"{rowIndex},{(int)RequestEnum.ParentPnNotExsit}");
                if (entity == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.SonPnNotExsit}");
                }

                if (checkp_pn == null && checkp_pn1 == null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.ParentPnNotExistInProductSture}");
                }

                if (checkmodel != null)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.SonPnNotExistInProductSture}");
                }
                if(string.IsNullOrWhiteSpace(p.quantity) || p.quantity.ToInt() <= 0)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.CountError}");
                }
                var repeats = ProductStructureLists.Where(t => t.pn == p.pn);
                if (repeats.Count() > 1)
                {
                    errors.Add($"{rowIndex},{(int)RequestEnum.SonPnExistInUploadFile}");
                }
                add_data.Add(p);
            }

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

            var count = accDbContext.siger_tr_product_structure.Count();
            var total = count > 0 ? accDbContext.siger_tr_product_structure.Max(t => t.id) : 0;
            int i = 1;
            add_data = new List<ProductStructureTemplate>();
            foreach (var p in ProductStructureLists)
            {
                add_data.Add(p);
                string sn = "";
                var model = accDbContext.siger_tr_product_structure.FirstOrDefault(t => t.pn == p.parent_pn && t.projectId == projectid && t.status == (int)RowState.Valid);
                var entity = accDbContext.siger_tr_materials.FirstOrDefault(t => t.pn == p.pn && t.status == (int)RowState.Valid && t.projectId == projectid);
                if (entity != null)
                {
                    var model1 = new ProductStructureTemplate();
                    if (model == null)
                    {
                        model1 = add_data.FirstOrDefault(t => t.pn == p.parent_pn);
                        if (model1 == null)
                        {
                            errors.Add($"{i},{(int)RequestEnum.ParentPnNotExistInUploadFile}");
                        }
                    }
                    var psEntity = new SigerTrProductStructure
                    {
                        id = total + i,
                        projectId = projectid,
                        parentid = model != null ? model.id : total + add_data.FindIndex(t => t.pn == p.parent_pn) + 1,
                        material_id = entity.id,
                        serial_number = sn ?? "",
                        name = entity.name ?? "",
                        spec = entity.spec ?? "",
                        pn = p.pn ?? "",
                        material_type_id = entity.typeid,
                        unit_id = entity.unit ?? "",
                        desc = p.desc ?? "",
                        quantity = p.quantity.ToInt(),
                        TransDateTime = UnixTimeHelper.GetNow()
                    };
                    entities.Add(psEntity);
                }
                i++;
            }
            if (errors.Any())
            {
                return new CommonImportResult(0, string.Join(";", errors));
            }
            var newentities = new List<SigerTrProductStructure>();
            var alldata = accDbContext.siger_tr_product_structure.Where(t => t.projectId == projectid && t.status == (int)RowState.Valid).ToList();
            //alldata.AddRange(entities);
            foreach (var p in entities)
            {
                var model = alldata.FirstOrDefault(t => t.id == p.parentid && t.projectId == projectid && t.status == (int)RowState.Valid);
                var sn = GetSerialNumber(model != null ? model.id : 0, projectid, alldata);
                p.serial_number = sn;
                newentities.Add(p);
                alldata.Add(p);//已有serial_number的数据添加到alldata，模拟一条条插入数据的操作
            }
            try
            {
                accDbContext.siger_tr_product_structure.AddRange(newentities);
                accDbContext.SaveChanges();
                return new CommonImportResult(1, "1");
            }
            catch
            {
                throw;
            }
        }

        private string GetSerialNumber(int pid, int projectid, List<SigerTrProductStructure> alldata)
        {
            string sn = "";
            if (pid > 0)
            {
                var entity = alldata.FirstOrDefault(t => t.id == pid && t.status == (int)RowState.Valid && t.projectId == projectid);
                if (entity != null && entity.parentid > 0)
                {
                    var list = alldata.Where(t => t.parentid == pid && t.status == (int)RowState.Valid && t.projectId == projectid);
                    sn = (list != null && list.Any()) ? (entity.serial_number + "." + (list.Count() + 1).ToString()) : (entity.serial_number + ".1");
                }
                else if (entity != null && entity.parentid == 0)
                {
                    var list = alldata.Where(t => t.parentid == pid && t.status == (int)RowState.Valid && t.projectId == projectid);
                    sn = (list != null && list.Any()) ? (list.Count() + 1).ToString() : "1";
                }
            }
            else
            {
                sn = "0";
            }
            return sn;
        }
    }
}
