using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Aurora.Common.Entities;
using Aurora.Tenant.Domain.Entities;
using Aurora.Infrastructure.Tenant;
using Aurora.Infrastructure.User;
using Microsoft.EntityFrameworkCore;

namespace Aurora.Tenant.EntityFramework
{
    public class ApplicationReadonlyDbContext : DbContext
    {
        protected readonly ITenantContext _tenantContext;

        public ApplicationReadonlyDbContext(DbContextOptions<ApplicationReadonlyDbContext> options
            , ITenantContextProvider currentTenantUserProvider) : base(options)
        {
            _tenantContext = currentTenantUserProvider.GetTenantContext();
        }
        /// <summary>
        /// 用于让子类继承时调用的构造函数
        /// </summary>
        /// <param name="options"></param>
        /// <param name="currentTenantUserProvider"></param>
        /// <returns></returns>
        protected ApplicationReadonlyDbContext(DbContextOptions options
            , ITenantContextProvider currentTenantUserProvider) : base(options)
        {
            _tenantContext = currentTenantUserProvider.GetTenantContext();
        }

        public DbSet<TenantUser> Categories { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            //配置正在运行的程序集里所有实体映射配置类
            builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());

            //多租户过滤
            foreach (var type in GetBaseEntityTypes())
            {
                var method = SetGlobalQueryMethod.MakeGenericMethod(type);
                method.Invoke(this, new object[] { builder });
            }

            // 配置表名映射
            builder.Entity<TenantUser>().ToTable("TenantUser");

            base.OnModelCreating(builder);
        }

        #region 多租户过滤
        private static IList<Type> _baseEntityTypesCache;

        private static IList<Type> GetBaseEntityTypes()
        {
            if (_baseEntityTypesCache != null)
                return _baseEntityTypesCache.ToList();
            var assembly = typeof(TenantEntityBase).GetTypeInfo().Assembly;
            // var assembly=Assembly.Load("Aurora.TenantUser.Domain");
            _baseEntityTypesCache = (from t in assembly.DefinedTypes
                                     where t.BaseType == typeof(TenantEntityBase)
                                     select t.AsType()).ToList();
            return _baseEntityTypesCache;
        }
        protected static readonly MethodInfo SetGlobalQueryMethod = typeof(ApplicationReadonlyDbContext).GetMethods(BindingFlags.Public | BindingFlags.Instance)
                                                        .Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");

        public void SetGlobalQuery<T>(ModelBuilder builder) where T : TenantEntityBase
        {
            builder.Entity<T>().HasQueryFilter(e => e.TenantCode == _tenantContext.TenantCode);
        }
        #endregion
    }
}