using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using IRaCIS.Core.Domain.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using AutoMapper.QueryableExtensions;
using EFCore.BulkExtensions;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.Data.SqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using IRaCIS.Core.Infra.EFCore.Common.Dto;

namespace IRaCIS.Core.Infra.EFCore
{
    public class Repository<TEntity> : IRepository<TEntity>
        where TEntity : Entity, new()
    {

        public IMapper _mapper { get; set; }
        public IRaCISDBContext _dbContext { get; set; }

        public DbSet<TEntity> _dbSet => _dbContext.Set<TEntity>();

        public IUserInfo _userInfo { get; set; }

        public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo)
        {
            _dbContext = dbContext;
            _mapper = mapper;
            _userInfo = userInfo;

        }


        #region 异步 EF 跟踪 添加
        public async Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities, bool isSaveAudit = false)
        {
            foreach (var addEntity in entities)
            {
                await AddAsync(addEntity, isSaveAudit);
            }

            return entities;
        }

        /// <summary>EF跟踪方式 添加</summary>
        public async ValueTask<TEntity> AddAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default, bool isSaveAudit = false)
        {

            await _dbSet.AddAsync(entity).ConfigureAwait(false);

            // 添加稽查
            //await AddInspectionAsync(entity, isSaveAudit);

            if (autoSave)
            {
                await SaveChangesAsync(cancellationToken);
            }

            return entity;
        }



        public async Task<TEntity> InsertFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
        {
            var entity = _mapper.Map<TEntity>(from);

            await EntityVerifyAsync(true, verify);


            entity = await AddAsync(entity, autoSave);


            return entity;

        }
        #endregion


        #region 异步 EF 跟踪  部分字段更新


        /// <summary>用前端传递的视图模型字段，更新，同时返回数据库该条记录的原始信息，方便对比某些字段是否更改，进行相应的逻辑操作</summary>

        public async Task<TEntity> UpdateFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
        {

            var entity = _mapper.Map<TEntity>(from);

            await EntityVerifyAsync(false, verify, entity.Id);

            var dbEntity = await _dbSet.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == entity.Id).ConfigureAwait(false);

            if (dbEntity == null)
            {
                throw new BusinessValidationFailedException(
                    " Update object not exist in db,Please check if the parameter Id is passed incorrectly");
            }

            var dbBeforEntity = dbEntity.Clone();


            _mapper.Map(from, dbEntity);


            //DTO  null  属性不更新  防止意外操作，导致保存数据错误,或者 add 和update 用一个模型，更新的时候，只传递了部分字段，导致，不想更新的字段，因为没传递值，用null覆盖了
            // Guid？属性 为null 时   映射到  Guid 时  默认会变成  Guid.Empty   
            if (ignoreDtoNullProperty)
            {
                var dbEntityProp = typeof(TEntity).GetProperties();
                foreach (var propertyInfo in from.GetType().GetProperties())
                {
                    if (propertyInfo.GetValue(from) == null && dbEntityProp.Any(t => t.Name == propertyInfo.Name))
                    {
                        _dbContext.Entry(dbEntity).Property(propertyInfo.Name).IsModified = false;
                    }
                }
            }

            await SaveChangesAsync(autoSave);


            return dbBeforEntity;
        }



        /// <summary> EF跟踪方式  生成 部分字段更新， 通过主键id  和表达式树 更新部分字段
        /// 例如  Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"),u => new Dictionary() { ParentId = null, Code = "test" }默认会去处理更新更新人 更新时间</summary>

        public async Task UpdatePartialNoQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
        {
            await SetPartialFieldUpdateAsync(id, updateFactory, verify);

            await SaveChangesAsync(autoSave);

        }


        /// <summary> EF跟踪方式  生成 部分字段立即更新，默认会去处理更新更新人 更新时间  </summary>
        public async Task<bool> UpdatePartialNowNoQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory,
            params EntityVerifyExp<TEntity>[] verify)
        {
            await SetPartialFieldUpdateAsync(id, updateFactory, verify);
            return await SaveChangesAsync(true);
        }


        /// <summary>注意 模型标注了 ConcurrencyCheck的属性，这样的实体，不适合用部分字段更新，ef生成的更新sql会自动带上ConcurrencyCheck的属性条件</summary>
        private async Task SetPartialFieldUpdateAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, params EntityVerifyExp<TEntity>[] verify)
        {
            await EntityVerifyAsync(false, verify, id);

            var entity = new TEntity() { Id = id };

            var entityEntry = _dbContext.Entry(entity);

            entityEntry.State = EntityState.Detached;


            List<PropertyInfo> list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
                .Select(propName => typeof(TEntity).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList();

            Func<TEntity, TEntity> func = updateFactory.Compile();

            TEntity applyObj = func(entity);

            foreach (PropertyInfo prop in list)
            {
                object value = prop.GetValue((object)applyObj);
                prop.SetValue((object)entity, value);

                _dbContext.Entry(entity).Property(prop.Name).IsModified = true;
            }

            #region Test


            //updateFactory.Compile()(entity);

            //List<string> propNameList = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name).ToList();

            //foreach (string propName in propNameList)
            //{
            //    _dbContext.Entry(entity).Property(propName).IsModified = true;
            //}

            #endregion
        }


        #endregion


        #region 异步  EF 跟踪 自动生成 更新 和删除语句


        /// <summary>EF跟踪方式 更新，全字段更新  不好</summary>
        public async Task<bool> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            _dbSet.Update(entity);

            if (autoSave)
            {
                return await SaveChangesAsync(cancellationToken);
            }
            else
            {
                return false;
            }
        }


        /// <summary>EF跟踪方式  外层先有查询好的完成实体，再更新部分字段  稽查的时候需要完整的实体信息</summary>
        public async Task<bool> UpdateAsync(TEntity waitModifyEntity, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            var entityEntry = _dbContext.Entry(waitModifyEntity);
            entityEntry.State = EntityState.Detached;


            ModifyPartialFiled(waitModifyEntity, updateFactory);

            return await SaveChangesAsync(autoSave);

        }


        /// <summary>EF跟踪方式  先查询出来，再更新部分字段 稽查的时候需要完整的实体信息</summary>
        public async Task<TEntity> UpdatePartialFromQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory,
            bool autoSave = false, bool ignoreQueryFilter = false, CancellationToken cancellationToken = default)
        {

            var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking();
            //不跟踪 查询出来的实体就是Detached
            var searchEntity = await query.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == id);

            if (searchEntity == null)
            {
                throw new BusinessValidationFailedException(
                    " Update object not exist in db,Please check if the parameter Id is passed incorrectly");
            }


            ModifyPartialFiled(searchEntity, updateFactory);


            await SaveChangesAsync(autoSave);

            return searchEntity;
        }

        public async Task UpdatePartialFromQueryAsync(Expression<Func<TEntity, bool>> updateFilter,
            Expression<Func<TEntity, TEntity>> updateFactory,
            bool autoSave = false, bool ignoreQueryFilter = false, CancellationToken cancellationToken = default)
        {
            if (updateFilter == null)
            {
                throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter));
            }
            var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking();

            var searchEntityList = await query.Where(updateFilter).ToListAsync();

            foreach (var needUpdateEntity in searchEntityList)
            {
                await UpdateAsync(needUpdateEntity, updateFactory, autoSave);
            }

        }


        /// <summary>更新后拥有完整的实体信息，便于稽查</summary>
        private void ModifyPartialFiled(TEntity waitModifyEntity, Expression<Func<TEntity, TEntity>> updateFactory)
        {
            List<PropertyInfo> list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
                .Select(propName => typeof(TEntity).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList();

            Func<TEntity, TEntity> func = updateFactory.Compile();

            TEntity applyObj = func(waitModifyEntity);

            //深拷贝更新之前实体信息
            //var copyObj = waitModifyEntity.Clone();

            foreach (PropertyInfo prop in list)
            {
                object value = prop.GetValue(applyObj);
                prop.SetValue(waitModifyEntity, value);

                _dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;
            }


        }


        /// <summary>EF跟踪方式 删除</summary>
        public async Task<bool> DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            _dbSet.Remove(entity);

            return await SaveChangesAsync(autoSave);
        }


        /// <summary>EF跟踪方式（查询出来，再删除 浪费性能,但是稽查 或者触发某些操作时，需要知道数据库实体信息 不可避免用这种）</summary>
        public async Task<TEntity> DeleteFromQueryAsync(Guid id, bool autoSave = false, bool ignoreQueryFilter = false)
        {
            var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking();

            var waitDelete = await query.Where(t => t.Id == id).FirstOrDefaultAsync();

            if (waitDelete == null)
            {
                throw new BusinessValidationFailedException(
                    " Delete object not exist in db,Please check if the parameter Id is passed incorrectly");
            }

            await DeleteAsync(waitDelete, autoSave);

            return waitDelete;
        }


        /// <summary>批量删除，EF跟踪方式（所有查询出来，再删除 浪费性能,但是稽查 或者触发某些操作时，需要知道数据库实体信息 不可避免用这种）</summary>
        public async Task<List<TEntity>> DeleteFromQueryAsync(Expression<Func<TEntity, bool>> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false)
        {
            var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking();
            var waitDeleteList = await query.Where(deleteFilter).ToListAsync();

            foreach (var deleteItem in waitDeleteList)
            {
                await DeleteAsync(deleteItem, false);
            }

            await SaveChangesAsync(autoSave);

            return waitDeleteList;

        }

        public async Task<List<TEntity>> SoftDeleteFromQueryAsync(Expression<Func<TEntity, bool>> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false)
        {
            var query = ignoreQueryFilter ? _dbSet.IgnoreQueryFilters() : _dbSet;
            var waitDeleteList =  await query.Where(deleteFilter).ToListAsync();
     
            foreach (var deleteItem in waitDeleteList)
            {
                if(deleteItem is ISoftDelete softDeleteItem)
                {
                    softDeleteItem.IsDeleted = true;
                }
            }

            await SaveChangesAsync(autoSave);

            return waitDeleteList;
        }

        #endregion



        #region 不走EF 跟踪机制的删除 更新  以及批量操作

        /// <summary>批量删除，相当于原生sql，  没用EF跟踪方式（所有查询出来，再删除 浪费性能）</summary>
        public async Task<bool> BatchDeleteNoTrackingAsync(Expression<Func<TEntity, bool>> deleteFilter)
        {
            return await _dbSet.IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0;
        }


        /// <summary>批量更新，相当于原生sql，  没用EF跟踪方式（所有查询出来，再更新 浪费性能）</summary>
        public async Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where,
            Expression<Func<TEntity, TEntity>> updateFactory)
        {

            var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();

            var hasPropNameList = bindings.Select(t => t.Member.Name).ToList();


            if (typeof(IAuditUpdate).IsAssignableFrom(typeof(TEntity)))
            {

                if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
                {
                    bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now)));

                }

                if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId)))
                {
                    bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(_userInfo.Id)));

                }
            }


            var member = Expression.MemberInit(Expression.New(typeof(TEntity)), bindings);

            var factory = Expression.Lambda<Func<TEntity, TEntity>>(member, Expression.Parameter(typeof(TEntity), "x"));


            return await _dbSet.IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory) > 0;



        }

        #endregion


        #region 保存 、忽略 、验证
        public async Task<TEntity> InsertOrUpdateAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
        {
            var entity = _mapper.Map<TEntity>(from);


            if (entity.Id == Guid.Empty)
            {
                return await InsertFromDTOAsync(from, autoSave, verify);
            }
            else
            {
                return await UpdateFromDTOAsync(from, autoSave, false, verify);
            }
        }
        private async Task EntityVerifyAsync(bool isAdd, EntityVerifyExp<TEntity>[] verify, Guid? entitydId = null)
        {

            if (isAdd)
            {
                foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
                {
                    if (await _dbSet.AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
                    {
                        throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                    }
                }
            }
            else
            {
                foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyAdd && t.IsVerify))
                {
                    if (verifyItem.verifyType == VerifyEnum.OnlyUpdate)
                    {
                        if (await _dbSet.AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                    else if (verifyItem.verifyType == VerifyEnum.Both)
                    {
                        if (await _dbSet.AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                }
            }

        }

        private async Task<bool> SaveChangesAsync(bool autoSave)
        {
            if (autoSave)
            {
                return await SaveChangesAsync();
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 忽略空值属性
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="ignoreNullValues"></param>
        private void IgnoreNullValues(ref TEntity entity, bool? ignoreNullValues = null)
        {
            var isIgnore = ignoreNullValues;
            if (isIgnore == false) return;

            // 获取所有的属性
            var properties = _dbSet.EntityType.GetProperties();
            if (properties == null) return;

            foreach (var propety in properties)
            {
                var entityProperty = _dbContext.Entry(entity).Property(propety.Name);
                var propertyValue = entityProperty?.CurrentValue;
                var propertyType = entityProperty?.Metadata?.PropertyInfo?.PropertyType;

                // 判断是否是无效的值，比如为 null，默认时间，以及空 Guid 值
                var isInvalid = propertyValue == null
                                || (propertyType == typeof(DateTime) && propertyValue?.ToString() == new DateTime().ToString())
                                || (propertyType == typeof(DateTimeOffset) && propertyValue?.ToString() == new DateTimeOffset().ToString())
                                || (propertyType == typeof(Guid) && propertyValue?.ToString() == Guid.Empty.ToString());

                if (isInvalid && entityProperty != null)
                {
                    entityProperty.IsModified = false;
                }
            }
        }


        public async Task<bool> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            return await _dbContext.SaveChangesAsync(cancellationToken) > 0;
        }


        #endregion

        #region 不常用
        /// <summary>EF跟踪方式  生成 部分字段更新  (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age))</summary>
        public async Task<TEntity> UpdatePartialFieldsAsync(TEntity entity, string[] propertyNames,
            bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
        {
            await EntityVerifyAsync(false, verify, entity.Id);

            var entityEntry = _dbContext.Entry(entity);
            entityEntry.State = EntityState.Detached;


            foreach (var propertyName in propertyNames)
            {
                _dbContext.Entry(entity).Property(propertyName).IsModified = true;
            }

            // 忽略空值
            IgnoreNullValues(ref entity, ignoreEntityNullProperty);

            return entityEntry.Entity;
        }

        /// <summary>更新  排除某些字段的更新  排除方式： new[] {nameof(User.Name), nameof(User.Age)</summary>
        public async Task<TEntity> UpdateExcludeFields(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
        {

            await EntityVerifyAsync(false, verify, entity.Id);

            var entityEntry = _dbContext.Entry(entity);
            entityEntry.State = EntityState.Modified;


            foreach (var propertyName in propertyNames)
            {
                _dbContext.Entry(entity).Property(propertyName).IsModified = false;
            }

            // 忽略空值
            IgnoreNullValues(ref entity, ignoreEntityNullProperty);

            return entityEntry.Entity;
        }


        #endregion

        #region 稽查


        /// <summary>
        /// 翻译稽查数据
        /// </summary>
        /// <param name="dto">传入Dto</param>
        /// <returns></returns>
        public async Task<List<string>> SetInspectionEnumValue(SetInspectionEnumValueDto dto)
        {
            var listIdentification = dto.Items.Select(x => x.Identification).ToList() ;
            foreach (var item in dto.Items)
            {
                if (item.Identification == string.Empty || item.Json == string.Empty)
                {
                    continue;
                }
                item.Json = await GetInspectionEnumValue(listIdentification, item.Json);
                item.Json = await SetEnum(dto.TrialId, listIdentification, item.Json);
                item.Json = await SetDataInspectionDateType(listIdentification, item.Json);
            }
            return dto.Items.Select(x => x.Json).ToList();
        }





        /// <summary>
        /// 格式化日期和时间
        /// </summary>
        /// <param name="Data">稽查数据</param>
        /// <returns></returns>
        public async Task<string> SetDataInspectionDateType(List<string> identification, string json)
        {
            var list = await (from parent in _dbContext.FrontAuditConfig.AsQueryable().Where(x => identification.Contains(x.Identification))
                              join child in _dbContext.FrontAuditConfig.AsQueryable().Where(x => x.EnumType == "Date" && x.IsEnable.HasValue && x.IsEnable.Value) on parent.Id equals child.ParentId
                              select new DateDto()
                              {
                                  Code = child.Code,
                                  DateType = child.DateType,
                              }).ToListAsync();

            list = list.GroupBy(x => new { x.Code }, (key, lst) => new DateDto()
            {
                Code = key.Code,
                DateType = lst.Max(x => x.DateType),
            }).ToList();

            var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(json);

            if (JsonData == null)
            {
                return json;
            }

            foreach (var item in JsonData.Keys)
            {
                var datefirst = list.FirstOrDefault(x => x.Code.ToLower() == item.ToLower());
                if (datefirst != null && !IsNullOrEmpty(JsonData[item]))
                {
                    try
                    {
                        if (datefirst.DateType == "Date")
                        {
                            JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd");
                        }

                        if (datefirst.DateType == "DateTime")
                        {
                            JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd HH:mm:ss");
                        }
                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }

            }

            return JsonConvert.SerializeObject(JsonData);

          
        }

        /// <summary>
        /// 获取外键表数据
        /// </summary>
        /// <param name="Table">表名称</param>
        /// <param name="ForeignKeyValue">外键value</param>
        /// <param name="ForeignKeyText">要查询的外键值</param>
        /// <param name="value">传入的纸</param>
        /// <returns></returns>
        public async Task<string> GetInspectionEnumValue(List<string> identification, string json)
        {
            var list = await (from u in _dbContext.FrontAuditConfig.Where(x => identification.Contains(x.Identification))
                              join p in _dbContext.FrontAuditConfig.Where(x => x.EnumType == "Foreign" && x.IsEnable.HasValue && x.IsEnable.Value) on u.Id equals p.ParentId
                              select new
                              {
                                  Key = p.Code,
                                  ForeignKeyValue = p.ForeignKeyValue,
                                  ForeignKeyText = p.ForeignKeyText,
                                  ForeignKeyTable = p.ForeignKeyTable
                              }).ToListAsync();
            
            list= list.GroupBy(x => new { x.Key }, (key, lst) => new 
            {
                Key=key.Key,
                ForeignKeyValue=lst.Max(x=>x.ForeignKeyValue),
                ForeignKeyText = lst.Max(x => x.ForeignKeyText),
                ForeignKeyTable = lst.Max(x => x.ForeignKeyTable),

            }).ToList();



            var JsonDataValue = JsonConvert.DeserializeObject<IDictionary<string, object>>(json);
            foreach (var item in list)
            {
                if (!JsonDataValue.ContainsKey(item.Key))
                {
                    continue;
                }
                string Table = item.ForeignKeyTable;
                string ForeignKeyValue = item.ForeignKeyValue;
                string ForeignKeyText = item.ForeignKeyText;
                if (JsonDataValue[item.Key] != null)
                {
                    string value = JsonDataValue[item.Key].ToString();
                    string para = string.Empty;
                    string sql = string.Empty;
                    var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(JsonConvert.SerializeObject(new { item = value }));
                    if (JsonData["item"].GetType() == typeof(JArray))
                    {
                        foreach (var v in JsonData["item"] as JArray)
                        {
                            para += para == string.Empty ? $"'{v.ToString()}'" : $",'{v.ToString()}'";
                        }
                        sql = $"select {ForeignKeyText} Text from {Table} where {ForeignKeyValue} in (@para)";
                    }
                    else
                    {
                        para = $"{JsonData["item"].ToString()}";
                        sql = $"select {ForeignKeyText}  Text from {Table} where {ForeignKeyValue} = @para";
                    }
                    SqlParameter[] paravalue = new SqlParameter[] {
                   new SqlParameter("@para",para)
                                             };
                    JsonDataValue[item.Key] = string.Join(",", _dbContext.Database.SqlQuery<ForeignKey>(sql, paravalue).Select(x => x.Text).ToList());
                }
               
            }
            return JsonConvert.SerializeObject(JsonDataValue);

        }


        /// <summary>
        /// 获取枚举
        /// </summary>
        /// <param name="Identification">标识</param>
        /// <param name="json">Json对象</param>
        /// <returns></returns>
        /// <exception cref="BusinessValidationFailedException"></exception>
        public async Task<string> SetEnum(Guid trilaid, List<string> identification, string json)
        {
            if (json == null||json=="null")
            {
                return null;
            }
            var list = await (from u in _dbContext.FrontAuditConfig.Where(x => identification.Contains(x.Identification))
                              join p in _dbContext.FrontAuditConfig.Where(x => x.Code != "AuditState" && x.EnumType == "Dictionary"&&x.IsEnable.HasValue&& x.IsEnable.Value) on u.Id equals p.ParentId
                              select new
                              {
                                  Key = p.Code,
                                  Code = p.DictionaryCode,
                                  Type = p.DictionaryType
                              }).ToListAsync();

            // 添加单双审
            var trialtype = await _dbContext.Trial.AsQueryable().Where(x => x.Id == trilaid).Select(x => x.QCProcessEnum).FirstOrDefaultAsync();

            list.Add(new
            {

                Key = "AuditState",
                Code = trialtype == TrialQCProcess.SingleAudit ? "AuditStatePE" : "AuditStateRC",
                Type = "Code",
            });


            list = list.GroupBy(x => new { x.Key }, (key, lst) => new
            {
                Key = key.Key,
                Code = lst.Max(x => x.Code),
                Type = lst.Max(x => x.Type),
            }).ToList();

            var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(json);
            foreach (var item in list)
            {
                try
                {

                    if (!JsonData.ContainsKey(item.Key) || JsonData[item.Key]==null)
                    {
                        continue;
                    }
                    var value = JsonData[item.Key];
                    if (value.GetType() == typeof(JArray))
                    {
                        JArray arrays = (JArray)value;
                        if (item.Type.ToLower() == "id".ToLower())
                        {
                            List<Guid> guids = new List<Guid>();
                            arrays.ForEach(x =>
                            {
                                guids.Add(Guid.Parse(x.ToString()));
                            });
                            JsonData[item.Key] = string.Join(',', await _dbContext.Dictionary.Where(x => guids.Contains(x.Id)).Select(x => x.ValueCN).ToListAsync());
                        }
                        else if (item.Type.ToLower() == "ChildGroup".ToLower())
                        {
                            List<string> guids = new List<string>();
                            arrays.ForEach(x =>
                            {
                                guids.Add(x.ToString());
                            });
                            JsonData[item.Key] = string.Join(',', await
                               _dbContext.Dictionary.Where(x => x.Code == item.Code).GroupJoin(
                                    _dbContext.Dictionary.Where(x => guids.Contains(x.ChildGroup)), a => a.Id, b => b.ParentId, (a, b) => new
                                    {
                                        parent = b
                                    }).SelectMany(a => a.parent, (m, n) => new
                                    {
                                        value = n.ValueCN
                                    }).Select(x => x.value).ToListAsync()
                                    );
                        }
                        else
                        {
                            List<string> guids = new List<string>();
                            arrays.ForEach(x =>
                            {
                                guids.Add(x.ToString());
                            });
                            JsonData[item.Key] = string.Join(',', await
                                _dbContext.Dictionary.Where(x => x.Code == item.Code).GroupJoin(
                                     _dbContext.Dictionary.Where(x => guids.Contains(x.Code)), a => a.Id, b => b.ParentId, (a, b) => new
                                     {
                                         parent = b
                                     }).SelectMany(a => a.parent, (m, n) => new
                                     {
                                         value = n.ValueCN
                                     }).Select(x => x.value).ToListAsync()
                                    );
                        }
                    }
                    else
                    {
                        if (item.Type.ToLower() == "id".ToLower())
                        {
                            Guid guid = Guid.Parse(value.ToString());
                            JsonData[item.Key] = await _dbContext.Dictionary.Where(x => guid == x.Id).Select(x => x.ValueCN).FirstOrDefaultAsync();
                        }
                        else if (item.Type.ToLower() == "ChildGroup".ToLower())
                        {
                            JsonData[item.Key] = await _dbContext.Dictionary.Where(x => x.Code == item.Code).Join(_dbContext.Dictionary.Where(x => x.ChildGroup == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new
                            {
                                value = b.ValueCN
                            }).Select(x => x.value).FirstOrDefaultAsync();
                        }
                        else
                        {
                            JsonData[item.Key] = await _dbContext.Dictionary.Where(x => x.Code == item.Code).Join(_dbContext.Dictionary.Where(x => x.Code == value.ToString()), a => a.Id, b => b.ParentId, (a, b) => new
                            {
                                value = b.ValueCN
                            }).Select(x => x.value).FirstOrDefaultAsync();

                        }
                    }
                }
                catch (Exception)
                {

                  
                }
            }



            return JsonConvert.SerializeObject(JsonData);




        }

     


        /// <summary>
        /// 格式化日期和时间
        /// </summary>
        /// <param name="Data">稽查数据</param>
        /// <returns></returns>
        public async Task<DataInspection> SetDataInspectionDateType(DataInspection Data)
        {
            var list = await (from parent in _dbContext.FrontAuditConfig.AsQueryable().Where(x => x.Identification == Data.Identification)
                              join child in _dbContext.FrontAuditConfig.AsQueryable().Where(x => x.EnumType == "Date") on parent.Id equals child.ParentId
                              select new DateDto()
                              {
                                  Code = child.Code,
                                  DateType = child.DateType,
                              }).ToListAsync();

            var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(Data.JsonDetail);

            foreach (var item in JsonData.Keys)
            {
                var datefirst = list.FirstOrDefault(x => x.Code.ToLower() == item.ToLower());
                if (datefirst != null && !IsNullOrEmpty(JsonData[item]))
                {
                    try
                    {
                        if (datefirst.DateType == "Date")
                        {
                            JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd");
                        }

                        if (datefirst.DateType == "DateTime")
                        {
                            JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd HH:mm:ss");
                        }
                    }
                    catch (Exception)
                    {
                        continue;
                    }
                }

            }
            Data.JsonDetail = JsonConvert.SerializeObject(JsonData);

            return Data;
        }

        /// <summary>
        /// IsNullOrEmpty
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private bool IsNullOrEmpty(object value)
        {
            if (value == null || value.ToString() == string.Empty)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 设置项目以及名称
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        public async Task SetInspectionNameValue(DataInspection Data)
        {
            #region 项目名称

            var trialdata = await _dbContext.Trial.Select(x => new { x.Id, x.ResearchProgramNo, x.ExperimentName, }).FirstOrDefaultAsync(x => x.Id == Data.TrialId);
            if (IsNullOrEmpty(Data.ResearchProgramNo))
            {

                Data.ResearchProgramNo = trialdata?.ResearchProgramNo;
            }

            if (IsNullOrEmpty(Data.TrialName))
            {
                Data.TrialName = trialdata?.ExperimentName;
            }
            #endregion

            #region 测试中心名称


            Data.SiteCode = (await _dbContext.TrialSite.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.TrialId == Data.TrialId && x.SiteId == Data.SiteId))?.TrialSiteCode;



            if (IsNullOrEmpty(Data.SiteName) && Data.SiteId != null)
            {
                var sitedata = await _dbContext.Site.Where(x => x.Id == Data.SiteId).Select(x => new { x.SiteName }).FirstOrDefaultAsync();
                Data.SiteName = sitedata?.SiteName;
            }
            #endregion

            #region 受试者


            if (IsNullOrEmpty(Data.SubjectCode) && Data.SubjectId != null)
            {

                Data.SubjectCode = (await _dbContext.Subject.Where(x => x.Id == Data.SubjectId).Select(x => new { x.Code }).FirstOrDefaultAsync())?.Code;
            }
            #endregion

            #region 访视
            if (IsNullOrEmpty(Data.SubjectVisitName))
            {
                Data.SubjectVisitName = (await _dbContext.SubjectVisit.Where(x => x.Id == Data.SubjectVisitId).Select(x => new { x.VisitName }).FirstOrDefaultAsync())?.VisitName;
            }
            #endregion

            #region 创建者

            if (IsNullOrEmpty(Data.CreateUserName))
            {
                Data.CreateUserName = _userInfo.RealName;
            }

            if (IsNullOrEmpty(Data.RoleName))
            {
                Data.RoleName = _userInfo.UserTypeShortName;
            }

            //if (IsNullOrEmpty(Data.CreateUserName) || IsNullOrEmpty(Data.RoleName))
            //{
            //    var userdata = await _dbContext.Users.AsQueryable().Where(x => x.Id == Data.CreateUserId).GroupJoin(_dbContext.UserType.AsQueryable(), a => a.UserTypeId, b => b.Id, (a, b) => new
            //    {
            //        UserName = a.FirstName + a.LastName,
            //        Role = b
            //    }).SelectMany(a => a.Role, (m, n) => new
            //    {
            //        UserName = m.UserName,
            //        RoleName = n.UserTypeShortName
            //    }).FirstOrDefaultAsync();

            //    if (userdata != null)
            //    {
            //        if (IsNullOrEmpty(Data.CreateUserName))
            //        {
            //            Data.CreateUserName = userdata?.UserName;
            //        }


            //        if (IsNullOrEmpty(Data.RoleName))
            //        {
            //            Data.RoleName = userdata?.RoleName;
            //        }
            //    }
            //}
            #endregion

            #region 取操作类型
            try
            {
                var from = await _dbContext.FrontAuditConfig.FirstOrDefaultAsync(x => x.Identification == Data.Identification);
                Data.ObjectType = from.ObjectTypeId;
                Data.OptType = from.OptTypeId;
                Data.ChildrenType = from.ChildrenTypeId;
                Data.ModuleType = from.ModuleTypeId;
            }
            catch (Exception)
            {

                throw new BusinessValidationFailedException("操作标识异常");
            }


            #endregion

        }



        #endregion


        #region 异步查询



        public async Task<TResult> MaxAsync<TResult>(Expression<Func<TEntity, TResult>> selector)
        {

            return await _dbSet.AsNoTracking().MaxAsync(selector);
        }

        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> exp, bool ignoreQueryFilters = false)
        {
            var query = _dbSet.AsQueryable();

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }

            return await query.AsNoTracking().AnyAsync(exp);
        }

        public async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereLambda = null, bool ignoreQueryFilters = false)
        {
            var query = _dbSet.AsQueryable();

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }

            return whereLambda == null ? await query.AsNoTracking().CountAsync() : await query.AsNoTracking().CountAsync(whereLambda);
        }

        public async ValueTask<TEntity> FindAsync(Guid id, CancellationToken cancellationToken = default)
        {
            return await _dbContext.FindAsync<TEntity>(id);
        }

        //有可能是联合主键，本项目没用到，用Guid
        public async ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
        {
            return await _dbContext.FindAsync<TEntity>(keyValues);
        }

        public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> exp = null, bool ignoreQueryFilters = false)
        {

            var query = _dbSet.AsQueryable();

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }


            if (exp != null)
            {
                query = query.Where(exp);
            }

            return await query.FirstOrDefaultAsync().ConfigureAwait(false);

        }

        /// <summary>
        /// 不跟踪
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="ignoreQueryFilters"></param>
        /// <returns></returns>
        public async Task<TEntity> FirstOrDefaultNoTrackingAsync(Expression<Func<TEntity, bool>> exp = null, bool ignoreQueryFilters = false)
        {

            var query = _dbSet.AsNoTracking().AsQueryable();

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }


            if (exp != null)
            {
                query = query.Where(exp);
            }

            return await query.AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false);

        }

        #endregion


        #region 非异步部分

        public TEntity ImageFind(Guid id, Type type)
        {
            //重传的时候  状态为修改    上传的时候状态为添加  内存中有，就不要重复查询数据库了
            var list = _dbContext.ChangeTracker.Entries()
                  .Where(u => (u.State == EntityState.Added || u.State == EntityState.Modified) && (u.Entity.GetType() == type)).Select(t => t.Entity as TEntity);

            var entity = list.FirstOrDefault(t => t.Id == id);

            if (entity == null)
            {
                return _dbSet.FirstOrDefault(t => t.Id == id);
            }
            else
            {
                return entity;
            }

        }

        public IQueryable<TEntity> AsQueryable(bool ignoreQueryFilters = false)
        {
            var query = _dbSet.AsQueryable();

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }

            return query.AsNoTracking();
        }

        public IQueryable<TResult> Select<TResult>(Expression<Func<TEntity, TResult>> selector)
        {
            return _dbSet.AsNoTracking().Select(selector);
        }

        public IQueryable<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> filter)
        {
            return condition ? _dbSet.AsNoTracking().Where(filter) : _dbSet.AsNoTracking();
        }

        public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false)
        {
            IQueryable<TEntity> query = _dbSet;

            if (!isTraking)
            {
                query = query.AsNoTracking();
            }

            if (ignoreQueryFilters)
            {
                query = query.IgnoreQueryFilters();
            }

            if (exp != null)
            {
                query = query.Where(exp);
            }
            return query;
        }

        public EntityEntry Entry(TEntity t)
        {
            return _dbContext.Entry(t);
        }

        public EntityEntry<TEntity> Attach(TEntity entity)
        {
            return _dbSet.Attach(entity);
        }

        public void Detached(TEntity t)
        {
            _dbContext.Entry(t).State = EntityState.Detached;
        }


        //  automapper 相关
        public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, object parameters, params Expression<Func<TDestination, object>>[] membersToExpand)
        {
            return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand);
        }

        public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, params Expression<Func<TDestination, object>>[] membersToExpand)
        {
            return _dbSet.AsNoTracking().ProjectTo(configuration, membersToExpand);
        }

        public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, IDictionary<string, object> parameters, params string[] membersToExpand)
        {
            return _dbSet.AsNoTracking().ProjectTo<TDestination>(configuration, parameters, membersToExpand);
        }


        #endregion

        #region 待废弃

        /// <summary>
        /// 仅仅供字典表插入使用，因为efcore 动态映射列的问题
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <param name="from"></param>
        /// <param name="verify"></param>
        /// <returns></returns>
        /// <exception cref="BusinessValidationFailedException"></exception>
        public async Task<TEntity> InsertDictionaryAsync<TFrom>(TFrom from, params EntityVerifyExp<TEntity>[] verify)
        {
            var entity = _mapper.Map<TEntity>(from);


            foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
            {
                if (await _dbSet.IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
                {
                    throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                }
            }

            if (typeof(TEntity) == typeof(Dictionary))
            {
                Type type = typeof(TFrom);

                //以下是不要ID这个字段的  比如自增列ID  就不能像上名那样写   
                var properties = type.GetProperties().Where(t => t.Name != "Id");


                string strSqlName = string.Join(",", properties.Select(p => $"[{p.Name}]").ToArray());

                string strSqlValue = string.Join(",", properties.Select(P => $"@{P.Name}").ToArray());

                string strSql = $"insert into {nameof(Dictionary)} ( " + strSqlName + " ) values (" + strSqlValue + ")";

                //para Sql是参数
                SqlParameter[] para = properties.Select(p => new SqlParameter($"@{p.Name}", p.GetValue(from, null))).ToArray();


                _dbContext.Database.ExecuteSqlRaw(strSql, para);

                return entity;
            }
            else
            {
                throw new Exception("仅仅供字典表插入使用，因为efcore 动态映射列的问题");
                //await _dbSet.BulkInsertAsync(new List<TEntity>() { entity });

                //return entity;
            }
        }

        public async Task<bool> AddRangeAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await _dbSet.AddRangeAsync(entities).ConfigureAwait(false);

            if (autoSave)
            {
                return await SaveChangesAsync(cancellationToken);
            }
            else
            {
                return false;
            }
        }




        #endregion
    }

}
