

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

namespace IRaCIS.Core.Infra.EFCore
{
    public interface IRepository<TEntity> : ICommandRepository<TEntity>, IQueryRepository<TEntity> where TEntity : Entity
    {
        IRaCISDBContext _dbContext { get; set; }

        /// <summary>
        /// 获取中心啥的名称
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        Task SetInspectionNameValue(DataInspection Data);

        /// <summary>
        /// 格式化日期和时间
        /// </summary>
        /// <param name="Data"></param>
        /// <returns></returns>
        Task<DataInspection> SetDataInspectionDateType(DataInspection Data);

        /// <summary>
        /// 翻译稽查数据
        /// </summary>
        /// <param name="dto">传入Dto</param>
        /// <returns></returns>
        Task<List<string>> SetInspectionEnumValue(SetInspectionEnumValueDto dto);
    }



    #region 泛型通用版本


    public interface IRepository
    {
        IQueryable<T> GetQueryable<T>(bool isTraking = false) where T : Entity;
        DbSet<T> Set<T>() where T : Entity;

        IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity;

        Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;

        Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;

        Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity;

        EntityEntry<T> Entry<T>(T t) where T : Entity;

        Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity;

        Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;
        Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;

        IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity;

        ValueTask<T> FindAsync<T>(Guid id) where T : Entity;

        ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity;

        Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
        Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;

        Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity;

        Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity;

        //Task<bool> DeleteManyAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;

        Task<bool> SaveChangesAsync();

        Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity;

        Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory) where T : Entity;
    }

    public class Repository : IRepository
    {
        private IRaCISDBContext _dbContext { get; }



        public IMapper _mapper { get; set; }

        public IUserInfo _userInfo { get; set; }

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

        /// <summary>
        /// 设置是使用哪个仓储 默认不跟踪
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="isTraking"></param>
        /// <returns></returns>
        
        public IQueryable<T> GetQueryable<T>(bool isTraking = false) where T : Entity
        {
            IQueryable<T> query = _dbContext.Set<T>();

            if (!isTraking)
            {
                query = query.AsNoTracking();
            }
            return query;
        }
        public DbSet<T> Set<T>() where T : Entity
        {
            return _dbContext.Set<T>();
        }

        public IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity
        {
            IQueryable<T> query = _dbContext.Set<T>().AsNoTracking();
            return condition ? query.Where(filter) : query;
        }


        private async Task EntityVerifyAsync<T>(bool isAdd, EntityVerifyExp<T>[] verify, Guid? entitydId = null) where T : Entity
        {

            if (isAdd)
            {
                foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
                {
                    if (await _dbContext.Set<T>().IgnoreQueryFilters().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 _dbContext.Set<T>().IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                    else if (verifyItem.verifyType == VerifyEnum.Both)
                    {
                        if (await _dbContext.Set<T>().IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                }
            }

        }

        public async Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false,
            params EntityVerifyExp<T>[] verify) where T : Entity
        {

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

            await EntityVerifyAsync(true, verify);


            entity = await AddAsync(entity, autoSave);


            return entity;
        }

        public async Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity
        {
            var entity = _mapper.Map<T>(from);

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

            var dbEntity = await _dbContext.Set<T>().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(T).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;
        }


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


        public async Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity
        {
            var entity = _mapper.Map<T>(from);


            if (entity.Id == Guid.Empty)
            {
                return await InsertFromDTOAsync(from, autoSave, verify);
            }
            else
            {
                return await UpdateFromDTOAsync(from, autoSave, false, verify);
            }
        }

        public EntityEntry<T> Entry<T>(T t) where T : Entity
        {
            return _dbContext.Entry<T>(t);
        }
        public async Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity
        {

            var query = _dbContext.Set<T>().AsQueryable();

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

            return await query.AsNoTracking().AnyAsync(filter).ConfigureAwait(false);

        }


      

        #region 基本查询 异步

        /// <summary>
        /// 跟踪查询某个实体  --异步  默认是跟踪查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exp"></param>
        /// <returns></returns>
        /// 
        public async Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
        {
            var query = _dbContext.Set<T>().AsQueryable();

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

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

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


        }
        public async Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
        {

            var query = _dbContext.Set<T>().AsQueryable();

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

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

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

        /// <summary>
        /// 过滤  默认不跟踪
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="exp"></param>
        /// <param name="isTraking"></param>
        /// <returns></returns>
        public IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity
        {
            IQueryable<T> query = _dbContext.Set<T>();

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

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

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

        /// <summary>
        ///  首先在内存中查找，其次再找数据库
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public async ValueTask<T> FindAsync<T>(Guid id) where T : Entity
        {
            return await _dbContext.Set<T>().FindAsync(id).ConfigureAwait(false);
        }

        #endregion

        #region 基本添加、更新、删除 异步

        public async ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity
        {

            await _dbContext.Set<T>().AddAsync(entity).ConfigureAwait(false);

            if (autoSave)
            {
                await SaveChangesAsync();
            }

            return entity;
        }

        public async Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
        {
            await _dbContext.Set<T>().AddRangeAsync(entities).ConfigureAwait(false);

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

        public async Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
        {
            _dbContext.Set<T>().UpdateRange(entities);

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

        public async Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity
        {
            _dbContext.Set<T>().Update(entity);

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


        public async Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity
        {
            _dbContext.Set<T>().Remove(entity);

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

        public async Task<bool> DeleteManyAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
        {
            _dbContext.Set<T>().RemoveRange(entities);

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

        public async Task<bool> SaveChangesAsync()
        {

            return await _dbContext.SaveChangesAsync().ConfigureAwait(false) > 0;

        }

        #endregion

        public async Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity
        {
            if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter));

            return await _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync().ConfigureAwait(false) > 0;
        }

        public async Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> whereFilter, Expression<Func<T, T>> updateFactory) where T : Entity
        {
            if (whereFilter == null) throw new ArgumentNullException(nameof(whereFilter));

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

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

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

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

                }

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

                }
            }


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

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

            return await _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0;
        }
    }

    #endregion




}
