using EFCore.BulkExtensions;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace IRaCIS.Core.Infra.EFCore
{
    public class EntityVerifyExp<TEntity> where TEntity : Entity
    {
        //验证表达式树
        public Expression<Func<TEntity, bool>> VerifyExp { get; set; }

        //验证提示错误信息
        public string VerifyMsg { get; set; }

        public VerifyEnum verifyType { get; set; } = VerifyEnum.Both;

        public bool IsVerify { get; set; } = true;
    }

    public enum VerifyEnum
    {
        OnlyAdd = 1,

        OnlyUpdate = 2,

        Both = 3,
    }




    public static class EntityAction
    {
         /// <summary>
         ///添加和更新的时候,通常需要与数据库已存在的数据进行校验,添加更新的区分在于是否需要排除自己
         /// </summary>
         /// <typeparam name="T"></typeparam>
         /// <param name="_dbContext"></param>
         /// <param name="isAdd"></param>
         /// <param name="verify"></param>
         /// <param name="entitydId"></param>
         /// <returns></returns>
         /// <exception cref="BusinessValidationFailedException"></exception>
        public static async Task EntityVerifyAsync<T>(this IRaCISDBContext _dbContext,  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>().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>().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                    else if (verifyItem.verifyType == VerifyEnum.Both)
                    {
                        if (await _dbContext.Set<T>().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
                        {
                            throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
                        }
                    }
                }
            }       
        }

        public static async Task EntityVerifyAsync<T>(this IRepository<T> _entityRepository,  Guid? entitydId = null,params EntityVerifyExp<T>[] verify) where T : Entity
        {

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



        ///注意 模型标注了 ConcurrencyCheck的属性,这样的实体,不适合用部分字段更新,ef生成的更新sql会自动带上ConcurrencyCheck的属性条件
        /// <summary>EntityState.Detached的实体   修改 部分字段</summary>
        public static void EntityModifyPartialFiled<T>(this IRaCISDBContext _dbContext,T waitModifyEntity, Expression<Func<T, T>> updateFactory) where T : Entity
        {
            var entityEntry = _dbContext.Entry(waitModifyEntity);
            //entityEntry.State = EntityState.Detached;

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

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

            T applyObj = func(waitModifyEntity);

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

            foreach (PropertyInfo prop in list)
            {
                _dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;

                object value = prop.GetValue(applyObj);
                prop.SetValue(waitModifyEntity, value);

            }


        }


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

        /// <summary>批量删除,相当于原生sql,  没用EF跟踪方式(所有查询出来,再删除 浪费性能)</summary>
        public static async Task<bool> BatchDeleteNoTrackingAsync<T>(this IRaCISDBContext _dbContext,Expression<Func<T, bool>> deleteFilter) where T : Entity
        {
            if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter));

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


        /// <summary>批量更新,相当于原生sql,  没用EF跟踪方式(所有查询出来,再更新 浪费性能)</summary>
        public static async Task<bool> BatchUpdateNoTrackingAsync<T>(this IRaCISDBContext _dbContext,Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory,Guid updateUserId) where T : Entity
        {
            if (where == null) throw new ArgumentNullException(nameof(where));

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

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


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

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

                }
            }


            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>().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0;



        }

        #endregion
    }






}