using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using EasyDevCore.Common;
using System.Data;
using System.Runtime.CompilerServices;
using System.Dynamic;
using System.Collections;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using System.Linq.Expressions;
using EasyDevCore.Database.EntityFrameworkCore;

namespace EasyDevCore.Database.EntityFrameworkCore
{
    /// <summary>
    /// 
    /// </summary>
    public static class DbSetExtensions
    {
        /// <summary>
        /// Gets the context.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="dbSet">The database set.</param>
        /// <returns></returns>
        public static DbContext GetContext<TEntity>(this DbSet<TEntity> dbSet) where TEntity : class
        {
            object internalSet = dbSet
                .GetType()
                .GetField("_internalSet", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(dbSet);
            object internalContext = internalSet
                .GetType()
                .BaseType
                .GetField("_internalContext", BindingFlags.NonPublic | BindingFlags.Instance)
                .GetValue(internalSet);
            return (DbContext)internalContext
                .GetType()
                .GetProperty("Owner", BindingFlags.Instance | BindingFlags.Public)
                .GetValue(internalContext, null);
        }

        /// <summary>
        /// Saves the change.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="dbSet">The database set.</param>
        /// <returns></returns>
        public static int SaveChange<TEntity>(this DbSet<TEntity> dbSet) where TEntity : class
        {
            var context = (EasyDbContext)dbSet.GetContext();
            return context.SaveChangeEntity(dbSet.GetType());
        }

        /// <summary>
        /// Saves the change asynchronous.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="dbSet">The database set.</param>
        /// <returns></returns>
        public static async Task<int> SaveChangeAsync<TEntity>(this DbSet<TEntity> dbSet) where TEntity : class
        {
            var context = (EasyDbContext)dbSet.GetContext();
            return await context.SaveChangeEntityAsync(dbSet.GetType()).ConfigureAwait(false);
        }
    }
}
