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.Extensions.Localization;

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

    }


    public class Repository<TEntity> : IRepository<TEntity>
        where TEntity : Entity, new()
    {

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

        public IStringLocalizer _localizer { get; set; }

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

        public IUserInfo _userInfo { get; set; }

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

        }




        #region 异步 EF 跟踪 添加
        public async Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities, bool autoSave = false)
        {

            await _dbSet.AddRangeAsync(entities).ConfigureAwait(false);

            await SaveChangesAsync(autoSave);

            return entities;
        }

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

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

            await SaveChangesAsync(autoSave);

            return entity;
        }



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

            await _dbContext.EntityVerifyAsync(true, verify);

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

            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);

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

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

            if (dbEntity == null)
            {
                
                throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]);
            }

            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跟踪方式  生成 部分字段更新， 跟踪的实体仅有修改的属性的值有具体意义，没有从数据库查询完整的实体</summary>

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

            await _dbContext.EntityVerifyAsync(false, verify, id);

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

            _dbContext.EntityModifyPartialFiled(entity, updateFactory);



            await SaveChangesAsync(autoSave);

        }


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

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

            _dbContext.EntityModifyPartialFiled(entity, updateFactory);

            return await SaveChangesAsync(true);
        }




        #endregion


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


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

            return await SaveChangesAsync(autoSave);
        }


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

            _dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory);

            return await SaveChangesAsync(autoSave);

        }


        /// <summary>EF跟踪方式  先查询出来，再更新部分字段  当在同一个事务里面需要更新同一个实体两次，请使用该方法，否则会因为重复跟踪同一个实体报错 （稽查的时候需要完整的实体信息）</summary>
        public async Task<TEntity> UpdatePartialFromQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory,
            bool autoSave = false,  CancellationToken cancellationToken = default)
        {

            //var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking();


            //不跟踪 查询出来的实体就是Detached
            var searchEntity = await _dbSet.FindAsync( id);

            if (searchEntity == null)
            {
                throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]);
            }

            _dbContext.EntityModifyPartialFiled(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("The update filter condition cannot be empty", 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, false);
            }

            await SaveChangesAsync(autoSave);

        }




        /// <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(_localizer["Repository_DeleteError"]);
            }

            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();

            _dbSet.RemoveRange(waitDeleteList);
            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 _dbContext.BatchDeleteNoTrackingAsync(deleteFilter);

        }


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

            return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory,_userInfo.Id);          

        }

        #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<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 _dbContext.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 _dbContext.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 异步查询



        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  bool Any(Expression<Func<TEntity, bool>> exp, bool ignoreQueryFilters = false)
        {
            var query = _dbSet.AsQueryable();

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

            return  query.AsNoTracking().Any(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> FirstAsync(Expression<Func<TEntity, bool>> exp = null, bool isTracking = false, bool ignoreQueryFilters = false)
        {

            var query = _dbSet.AsQueryable();

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

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

            var entity = await query.FirstOrDefaultAsync(exp);

            if (entity is null)
            {
                throw new QueryBusinessObjectNotExistException($"The query object {typeof(TEntity).Name} does not exist in database,  Please check the query parameters");
            }
            else
            {
                return entity;
            }

        }

        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
    }

}
