using Microsoft.EntityFrameworkCore;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace IRaCIS.Core.Infra.EFCore
{
    public static class DbContextExt
    {

        /// <summary>
        /// 获取变化的实体信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetChanges<T>(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && e.Entity is T).Select(e =>
            {
                var originalObject = e.OriginalValues.ToObject();
                var currentObject = e.CurrentValues.ToObject();
                return new ChangeEntry
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.OriginalValues.EntityType.ClrType,
                    ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
                    {
                        PropertyInfo = t1.Property.PropertyInfo,
                        OriginalValue = t1.Value,
                        CurrentValue = t2.Value,
                        IsPrimaryKey = t1.Property.IsPrimaryKey(),
                        IsForeignKey = t1.Property.IsForeignKey()
                    }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
                };
            });
        }



        /// <summary>
        /// 获取变化的实体信息
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetChanges(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified).Select(e =>
            {
                var originalObject = e.OriginalValues.ToObject();
                var currentObject = e.CurrentValues.ToObject();
                return new ChangeEntry()
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.OriginalValues.EntityType.ClrType,
                    ChangeProperties = e.OriginalValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(originalObject))).Zip(e.CurrentValues.Properties.Select(p => (Property: p, Value: p.PropertyInfo.GetValue(currentObject))), (t1, t2) => new ChangePropertyInfo()
                    {
                        PropertyInfo = t1.Property.PropertyInfo,
                        OriginalValue = t1.Value,
                        CurrentValue = t2.Value,
                        IsPrimaryKey = t1.Property.IsPrimaryKey(),
                        IsForeignKey = t1.Property.IsForeignKey(),
                    }).Where(t => Comparer.Default.Compare(t.OriginalValue, t.CurrentValue) != 0).ToList()
                };
            });
        }

        /// <summary>
        /// 获取添加的实体信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetAdded<T>(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added && e.Entity is T).Select(e =>
            {
                var currentObject = e.CurrentValues.ToObject();
                return new ChangeEntry
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.CurrentValues.EntityType.ClrType,
                    ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
                    {
                        PropertyInfo = p.PropertyInfo,
                        CurrentValue = p.PropertyInfo.GetValue(currentObject),
                        IsPrimaryKey = p.IsPrimaryKey(),
                        IsForeignKey = p.IsForeignKey(),
                    }).ToList()
                };
            });
        }

        /// <summary>
        /// 获取添加的实体信息
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetAdded(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Added).Select(e =>
            {
                var currentObject = e.CurrentValues.ToObject();
                return new ChangeEntry
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.CurrentValues.EntityType.ClrType,
                    ChangeProperties = e.CurrentValues.Properties.Select(p => new ChangePropertyInfo()
                    {
                        PropertyInfo = p.PropertyInfo,
                        CurrentValue = p.PropertyInfo.GetValue(currentObject),
                        IsPrimaryKey = p.IsPrimaryKey(),
                        IsForeignKey = p.IsForeignKey(),
                    }).ToList()
                };
            });
        }

        /// <summary>
        /// 获取移除的实体信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetRemoved<T>(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted && e.Entity is T).Select(e =>
            {
                var originalObject = e.OriginalValues.ToObject();
                return new ChangeEntry
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.OriginalValues.EntityType.ClrType,
                    ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
                    {
                        PropertyInfo = p.PropertyInfo,
                        OriginalValue = p.PropertyInfo.GetValue(originalObject),
                        IsPrimaryKey = p.IsPrimaryKey(),
                        IsForeignKey = p.IsForeignKey(),
                    }).ToList()
                };
            });
        }

        /// <summary>
        /// 获取移除的实体信息
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetRemoved(this DbContext db)
        {
            return db.ChangeTracker.Entries().Where(e => e.State == EntityState.Deleted).Select(e =>
            {
                var originalObject = e.OriginalValues.ToObject();
                return new ChangeEntry
                {
                    EntityState = e.State,
                    Entity = e.Entity,
                    EntityType = e.OriginalValues.EntityType.ClrType,
                    ChangeProperties = e.OriginalValues.Properties.Select(p => new ChangePropertyInfo()
                    {
                        PropertyInfo = p.PropertyInfo,
                        OriginalValue = p.PropertyInfo.GetValue(originalObject),
                        IsPrimaryKey = p.IsPrimaryKey(),
                        IsForeignKey = p.IsForeignKey(),
                    }).ToList()
                };
            });
        }

        /// <summary>
        /// 获取所有的变更信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetAllChanges<T>(this DbContext db)
        {
            return GetChanges<T>(db).Union(GetAdded<T>(db)).Union(GetRemoved<T>(db));
        }

        /// <summary>
        /// 获取所有的变更信息
        /// </summary>
        /// <param name="db"></param>
        /// <returns></returns>
        public static IEnumerable<ChangeEntry> GetAllChanges(this DbContext db)
        {
            return GetChanges(db).Union(GetAdded(db)).Union(GetRemoved(db));
        }
    }

    public class ChangePropertyInfo
    {
        /// <summary>
        /// 属性
        /// </summary>
        public PropertyInfo PropertyInfo { get; set; }

        /// <summary>
        /// 原始值
        /// </summary>
        public object OriginalValue { get; set; }

        /// <summary>
        /// 新值
        /// </summary>
        public object CurrentValue { get; set; }

        /// <summary>
        /// 是否是主键
        /// </summary>
        public bool IsPrimaryKey { get; set; }

        /// <summary>
        /// 是否是外键
        /// </summary>
        public bool IsForeignKey { get; set; }
    }

    public class ChangeEntry
    {
        /// <summary>
        /// 所属实体
        /// </summary>
        public object Entity { get; set; }

        /// <summary>
        /// 实体类型
        /// </summary>
        public Type EntityType { get; set; }

        /// <summary>
        /// 变更类型
        /// </summary>
        public EntityState EntityState { get; set; }

        /// <summary>
        /// 字段变更信息
        /// </summary>
        public List<ChangePropertyInfo> ChangeProperties { get; set; }
    }
}