using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Reflection;
using System.Threading.Tasks;
using System.Transactions;
namespace Utility
{
    /// 
    /// 工作单元配置特性
    /// 
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class UnitOfWorkAttribute : Attribute, IAsyncActionFilter, IAsyncPageFilter, IOrderedFilter
    {
        /// 
        /// 确保事务可用
        /// 此方法为了解决静态类方式操作数据库的问题
        /// 
        public bool EnsureTransaction { get; set; } = false;
        /// 
        /// 是否使用分布式环境事务
        /// 
        public bool UseAmbientTransaction { get; set; } = false;
        /// 
        /// 分布式环境事务范围
        /// 
        ///  为 true 有效
        public TransactionScopeOption TransactionScope { get; set; } = TransactionScopeOption.Required;
        /// 
        /// 分布式环境事务隔离级别
        /// 
        ///  为 true 有效
        public IsolationLevel TransactionIsolationLevel { get; set; } = IsolationLevel.ReadCommitted;
        /// 
        /// 分布式环境事务超时时间
        /// 
        /// 单位秒
        public int TransactionTimeout { get; set; } = 0;
        /// 
        /// 支持分布式环境事务异步流
        /// 
        ///  为 true 有效
        public TransactionScopeAsyncFlowOption TransactionScopeAsyncFlow { get; set; } = TransactionScopeAsyncFlowOption.Enabled;
        /// 
        ///  MiniProfiler 分类名
        /// 
        private const string MiniProfilerCategory = "unitOfWork";
        /// 
        /// 过滤器排序
        /// 
        private const int FilterOrder = 9999;
        /// 
        /// 排序属性
        /// 
        public int Order => FilterOrder;
        /// 
        /// 构造函数
        /// 
        public UnitOfWorkAttribute()
        {
        }
        /// 
        /// 构造函数
        /// 
        /// 
        public UnitOfWorkAttribute(bool ensureTransaction)
        {
            EnsureTransaction = ensureTransaction;
        }
        /// 
        /// 拦截请求
        /// 
        /// 动作方法上下文
        /// 中间件委托
        /// 
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // 获取动作方法描述器
            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            var method = actionDescriptor.MethodInfo;
            // 创建分布式环境事务
            (var transactionScope, var logger) = CreateTransactionScope(context);
            try
            {
                // 打印工作单元开始消息
                Console.WriteLine("Beginning (Ambient)");
                // 开始事务
                BeginTransaction(context, method, out var _unitOfWork, out var unitOfWorkAttribute);
                // 获取执行 Action 结果
                var resultContext = await next();
                // 提交事务
                CommitTransaction(context, _unitOfWork, unitOfWorkAttribute, resultContext);
                // 提交分布式环境事务
                if (resultContext.Exception == null)
                {
                    transactionScope?.Complete();
                    // 打印事务提交消息
                    if (UseAmbientTransaction) Console.WriteLine("Completed (Ambient)");
                }
                else
                {
                    // 打印事务回滚消息
                    if (UseAmbientTransaction) Console.WriteLine("Rollback (Ambient)");
                    logger.LogError(resultContext.Exception, "Transaction Failed.");
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Transaction Failed.");
                // 打印事务回滚消息
                if (UseAmbientTransaction) Console.WriteLine("Rollback (Ambient)");
                throw;
            }
            finally
            {
                transactionScope?.Dispose();
            }
        }
        /// 
        /// 模型绑定拦截
        /// 
        /// 
        /// 
        public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
        {
            return Task.CompletedTask;
        }
        /// 
        /// 拦截请求
        /// 
        /// 
        /// 
        /// 
        public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next)
        {
            // 获取动作方法描述器
            var method = context.HandlerMethod?.MethodInfo;
            // 处理 Blazor Server
            if (method == null)
            {
                _ = await next.Invoke();
                return;
            }
            // 创建分布式环境事务
            (var transactionScope, var logger) = CreateTransactionScope(context);
            try
            {
                // 打印工作单元开始消息
                if (UseAmbientTransaction) Console.WriteLine("Beginning (Ambient)");
                // 开始事务
                BeginTransaction(context, method, out var _unitOfWork, out var unitOfWorkAttribute);
                // 获取执行 Action 结果
                var resultContext = await next.Invoke();
                // 提交事务
                CommitTransaction(context, _unitOfWork, unitOfWorkAttribute, resultContext);
                // 提交分布式环境事务
                if (resultContext.Exception == null)
                {
                    transactionScope?.Complete();
                    // 打印事务提交消息
                    if (UseAmbientTransaction) Console.WriteLine("Completed (Ambient)");
                }
                else
                {
                    // 打印事务回滚消息
                    if (UseAmbientTransaction) Console.WriteLine("Rollback (Ambient)");
                    logger.LogError(resultContext.Exception, "Transaction Failed.");
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Transaction Failed.");
                // 打印事务回滚消息
                if (UseAmbientTransaction) Console.WriteLine("Rollback (Ambient)");
                throw;
            }
            finally
            {
                transactionScope?.Dispose();
            }
        }
        /// 
        /// 创建分布式环境事务
        /// 
        /// 
        /// 
        private (TransactionScope, ILogger) CreateTransactionScope(FilterContext context)
        {
            // 是否启用分布式环境事务
            var transactionScope = UseAmbientTransaction
                 ? new TransactionScope(TransactionScope,
                new TransactionOptions { IsolationLevel = TransactionIsolationLevel, Timeout = TransactionTimeout > 0 ? TimeSpan.FromSeconds(TransactionTimeout) : default }
                , TransactionScopeAsyncFlow)
                 : default;
            // 创建日志记录器
            var logger = context.HttpContext.RequestServices.GetRequiredService>();
            return (transactionScope, logger);
        }
        /// 
        /// 开始事务
        /// 
        /// 
        /// 
        /// 
        /// 
        private static void BeginTransaction(FilterContext context, MethodInfo method, out IUnitOfWork _unitOfWork, out UnitOfWorkAttribute unitOfWorkAttribute)
        {
            // 解析工作单元服务
            _unitOfWork = context.HttpContext.RequestServices.GetRequiredService();
            // 获取工作单元特性
            unitOfWorkAttribute = method.GetCustomAttribute();
            // 调用开启事务方法
            _unitOfWork.BeginTransaction(context, unitOfWorkAttribute);
            // 打印工作单元开始消息
            if (!unitOfWorkAttribute.UseAmbientTransaction) Console.WriteLine("Beginning");
        }
        /// 
        /// 提交事务
        /// 
        /// 
        /// 
        /// 
        /// 
        private static void CommitTransaction(FilterContext context, IUnitOfWork _unitOfWork, UnitOfWorkAttribute unitOfWorkAttribute, FilterContext resultContext)
        {
            // 获取动态结果上下文
            dynamic dynamicResultContext = resultContext;
            if (dynamicResultContext.Exception == null)
            {
                // 调用提交事务方法
                _unitOfWork.CommitTransaction(resultContext, unitOfWorkAttribute);
            }
            else
            {
                // 调用回滚事务方法
                _unitOfWork.RollbackTransaction(resultContext, unitOfWorkAttribute);
            }
            // 调用执行完毕方法
            _unitOfWork.OnCompleted(context, resultContext);
            // 打印工作单元结束消息
            if (!unitOfWorkAttribute.UseAmbientTransaction) Console.WriteLine("Ending");
        }
    }
}