using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Repository.Data;
using Siger.Middlelayer.Repository.Entities;
using Siger.Middlelayer.Repository.Paged;
using Siger.Middlelayer.Repository.Repositories.Interface;
using Siger.Middlelayer.Utility.ImportEntities;

namespace Siger.Middlelayer.Repository.Repositories
{
    internal class SigerTrMetrialTypeRepository : ApiConfigRepositoryBase<siger_tr_material_type>, ISigerTrMaterialTypeRepository
    {
        private readonly ApiConfigDbContext metrialTypeContext;
        public SigerTrMetrialTypeRepository(ApiConfigDbContext context) : base(context)
        {
            metrialTypeContext = context;
        }

        private IEnumerable<siger_tr_material_type> GetSonMaterialTypes(int parentId, int projectid)
        {
            var query = from c in metrialTypeContext.siger_tr_material_type
                        where c.parentid == parentId && c.projectId == projectid && c.status == (int)RowState.Valid
                        select c;

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

        private int GetMetrialCount(int parentId, int projectid)
        {
            var typeIds = GetSonMaterialTypes(parentId, projectid).Select(m => m.id).ToList();
            typeIds.Add(parentId);
            return metrialTypeContext.siger_tr_materials.Count(q =>
                typeIds.Contains(q.typeid) && q.projectId == projectid && q.status == (int)RowState.Valid);
        }

        public IPagedCollectionResult<MaterialType> GetPagedList(int parentType, int projectid, int page, int pagesize)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int) LanguageType.EN);
            var allMaterials = transHelper.GetTranslateText(TranslateEnEnum.AllMaterials);
            var query0 = metrialTypeContext.siger_tr_material_type.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid
                                                                                                        && q.parentid == 0);

            var querylist0 = from q in query0
                             select new MaterialType
                             {
                                 id = q.id,
                                 tcode = q.tcode,
                                 pid = q.parentid,
                                 name = q.name,
                                 pname = allMaterials
                             };

            var query1 = metrialTypeContext.siger_tr_material_type.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid
                                                                                                        && q.parentid != 0);
            var querylist1 = from q in query1
                             join p in metrialTypeContext.siger_tr_material_type on q.parentid equals p.id
                             into temp
                             from tt in temp.DefaultIfEmpty()
                             select new MaterialType
                             {
                                 id = q.id,
                                 tcode = q.tcode,
                                 pid = q.parentid,
                                 name = q.name,
                                 pname = (tt == null || string.IsNullOrEmpty(tt.name)) ? (q.parentid == 0 ? allMaterials : "") : tt.name
                             };


            Expression<Func<MaterialType, bool>> parentidExpression = q => true;
            if (parentType > 0) // 取全部子级
            {
                var ids = GetMaterialTypeIds(parentType, projectid);
                ids.Add(parentType);
                parentidExpression = q => ids.Contains(q.id);
            }

            var predicate = parentidExpression;

            var querylist = querylist0.Union(querylist1).Where(predicate).AsNoTracking().ToList();

            var entities = new List<MaterialType>();
            if (!(parentType > 0))
            {
                entities.Add(new MaterialType
                {
                    id = 0,
                    pid = -1,
                    name = allMaterials,
                    pname = "TOP",
                    tcode = CommonConst.MATERIALTYPE_PARENTNAME
                });
            }
            entities.AddRange(querylist);
            var totalCount = entities.Count();
            var list = entities.OrderBy(q => q.pid).Skip((page - 1) * pagesize).Take(pagesize);

            return new PagedCollectionResult<MaterialType>(list, totalCount);
        }

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

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

            return list;
        }

        private IEnumerable<siger_tr_material_type> GetSonGetMaterialTypes(int parentId, int projectid)
        {
            var query = from c in metrialTypeContext.siger_tr_material_type
                        where c.parentid == parentId && c.projectId == projectid && c.status == (int)RowState.Valid
                        select c;

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

        public IEnumerable<MaterialType> GetMaterialTypeList(int projectid)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int)LanguageType.EN);
            var allMaterials = transHelper.GetTranslateText(TranslateEnEnum.AllMaterials);
            var materialTypes = GetSonMaterialTypes(0, projectid);

            var query = from q in materialTypes
                        join p in metrialTypeContext.siger_tr_material_type on q.parentid equals p.id into temp
                        from tt in temp.DefaultIfEmpty()
                        select new MaterialType
                        {
                            id = q.id,
                            tcode = q.tcode,
                            pid = q.parentid,
                            name = q.name,
                            pname = (tt == null || string.IsNullOrEmpty(tt.name)) ? (q.parentid == 0 ? allMaterials : "") : tt.name
                        };

            return query.ToList();
        }

        public CommonImportResult ImportMaterialTypes(IEnumerable<MaterialTypeList> materialTypes, int projectid)
        {
            var entities = new List<siger_tr_material_type>();
            var materialTypeLists = materialTypes.ToList();
            foreach (var materialType in materialTypeLists)
            {
                var entity = metrialTypeContext.siger_tr_material_type.FirstOrDefault(q => q.name == materialType.Name
                                                                                     && q.projectId == projectid
                                                                                     && q.status == (int)RowState.Valid);

                var parententity = metrialTypeContext.siger_tr_material_type.FirstOrDefault(q => q.name == materialType.ParentType
                                                                                            && q.projectId == projectid
                                                                                            && q.status == (int)RowState.Valid);
                if (entity == null && parententity != null)
                {
                    var materialTypeEntity = new siger_tr_material_type
                    {
                        parentid = parententity.id,
                        projectId = projectid,
                        name = materialType.ParentType ?? "",
                        tcode = materialType.Tcode ?? "",
                        status = (int)RowState.Valid,
                        transdatetime = DateTime.Now
                    };
                    metrialTypeContext.siger_tr_material_type.Add(materialTypeEntity);
                }
            }

            try
            {
                metrialTypeContext.siger_tr_material_type.AddRange(entities);
                metrialTypeContext.SaveChanges();
                return new CommonImportResult(1, "1");
            }
            catch
            {
                throw;
            }
        }

        public IEnumerable<MaterialType> ExportMaterialTypes(int parentType, int projectid)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int)LanguageType.EN);
            var allMaterials = transHelper.GetTranslateText(TranslateEnEnum.AllMaterials);
            var list = new List<MaterialType>();
            var query0 = metrialTypeContext.siger_tr_material_type.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid
                                                                                                        && q.parentid == 0);

            var querylist0 = from q in query0
                             select new MaterialType
                             {
                                 id = q.id,
                                 tcode = q.tcode,
                                 pid = q.parentid,
                                 pname = allMaterials,
                                 name = q.name
                             };

            var query1 = metrialTypeContext.siger_tr_material_type.Where(q => q.status == (int)RowState.Valid && q.projectId == projectid
                                                                                                        && q.parentid != 0);
            var querylist1 = from q in query1
                             join c in metrialTypeContext.siger_tr_material_type on q.parentid equals c.id
                             into temp
                             from tt in temp.DefaultIfEmpty()
                             select new MaterialType
                             {
                                 id = q.id,
                                 tcode = q.tcode,
                                 pid = q.parentid,
                                 pname = (tt == null || string.IsNullOrEmpty(tt.name)) ? (q.parentid == 0 ? allMaterials : "") : tt.name,
                                 name = q.name
                             };

            Expression<Func<MaterialType, bool>> parentidExpression = q => true;
            if (parentType >= 0) // 取全部子级
            {
                var ids = GetMaterialTypeIds(parentType, projectid);
                parentidExpression = q => ids.Contains(q.id);
            }

            var predicate = parentidExpression;

            var querylist = querylist0.Union(querylist1).Where(predicate);

            var entities = querylist.Where(predicate).AsNoTracking().ToList();

            foreach (var materialType in entities)
            {
                list.Add(new MaterialType
                {
                    tcode = materialType.tcode,
                    id = materialType.id,
                    pid = materialType.pid,
                    pname = materialType.pname,
                    name = materialType.name
                });
            }

            return list;
        }

        public IEnumerable<MaterialTypeTree> GetMaterialTypeTree(int projectid)
        {
            var transHelper = new TranslateHelper(GetProjectLanguage(projectid) == (int)LanguageType.EN);
            var allMaterials = transHelper.GetTranslateText(TranslateEnEnum.AllMaterials);
            var list = new List<MaterialTypeTree>();
            list.Add(new MaterialTypeTree
            {
                id = 0,
                name = allMaterials,
                pid = -1,
                open = true
            });
            var materialTypes = GetSonMaterialTypes(0, projectid);

            foreach (var material in materialTypes.ToList())
            {
                list.Add(new MaterialTypeTree { id = material.id, name = material.name, pid = material.parentid, open = true });
            }

            return list;
        }

        public int InsertMaterialType(siger_tr_material_type materialType)
        {
            metrialTypeContext.siger_tr_material_type.Add(materialType);
            return metrialTypeContext.SaveChanges();
        }

        public int UpdateMaterialType(siger_tr_material_type materialType)
        {
            metrialTypeContext.siger_tr_material_type.Update(materialType);
            return metrialTypeContext.SaveChanges();
        }

        public int UpdateMaterialTypeRange(IEnumerable<siger_tr_material_type> materialTypes)
        {
            metrialTypeContext.siger_tr_material_type.UpdateRange(materialTypes);
            return metrialTypeContext.SaveChanges();
        }
    }
}
