using EasyCaching.Core;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Text.RegularExpressions;
using static IRaCIS.Core.Domain.Share.StaticData;

namespace IRaCIS.Core.Application.Filter
{
    /// <summary>
    /// 主要为了 处理项目结束 锁库，不允许操作
    /// </summary>
    public class TrialResourceFilter : Attribute, IAsyncResourceFilter
    {
        private readonly IEasyCachingProvider _provider;
        private readonly IUserInfo _userInfo;


        private readonly List<string> _trialOptList=new List<string>();


        public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
        {
            _provider = provider;
            _userInfo = userInfo;
            //_trialOpt = trialOpt;

            if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
            if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
            if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());

          
        }



        //public TrialResourceFilter(IEasyCachingProvider provider, IUserInfo userInfo)
        //{
        //    _provider = provider;
        //    _userInfo = userInfo;
        //}





        //优先选择异步的方法
        public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
        {
           // var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
           //var  _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();

            #region 处理新的用户类型，不能操作项目相关接口

            // 后期列举出具体的类型，其他任何用户类型，都不允许操作
            if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA)
            {
                context.Result = new JsonResult(ResponseOutput.NotOk("对不起，您的账户没有操作权限。"));

                return;
            }

            #endregion



            //TrialId 传递的途径多种，可能在path 可能在body  可能在数组中，也可能在对象中，可能就在url
            var trialIdStr = string.Empty;

            if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
            {
                trialIdStr = context.HttpContext.Request.Query["trialId"];
            }

            //先尝试从path中取TrialId 
            else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
            {
                var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
                trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
            }
            else if (context.HttpContext.Request.Headers["RefererUrl"].ToString().Contains("trialId"))
            {
                var headerStr = context.HttpContext.Request.Headers["RefererUrl"].ToString();

                var trialIdIndex = headerStr.IndexOf("trialId");


                var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");

                if (matchResult.Success)
                {
                    trialIdStr = matchResult.Value;
                }
                else
                {
                    context.Result = new JsonResult(ResponseOutput.NotOk("正则取请求Refer 中trialId 失败，请联系开发人员核查"));
                }

            }
            else
            {
                #region body 中取数据

                //设置可以多次读
                context.HttpContext.Request.EnableBuffering();
                var reader = new StreamReader(context.HttpContext.Request.Body);
                var contentFromBody = await reader.ReadToEndAsync();
                //读取后，流的位置还原
                context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
                //context.HttpContext.Request.Body.Position = 0;

                //找到参数位置在字符串中的索引
                var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);

                if (trialIdIndex > -1)
                {
                    //  (?<="trialId" *: *").*?(?=",)

                    //使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}

                    var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");

                    if (matchResult.Success)
                    {
                        //有可能匹配错误   "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236"  从缓存里面验证下
                        var cacheResultDic = _provider.GetAll<string>(new[] { matchResult.Value });

                        var trialStatusStr = cacheResultDic[matchResult.Value].Value;

                        if (!string.IsNullOrWhiteSpace(trialStatusStr))
                        {
                            trialIdStr = matchResult.Value;
                        }


                    }
                    else
                    {
                        context.Result = new JsonResult(ResponseOutput.NotOk("正则取请求Refer 中trialId 失败，请联系开发人员核查"));
                    }

                    //使用字符串取 如果是swagger  可能有时取的不对  因为空格的原因
                    //trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
                }

                #endregion
            }

            //通过path 或者body 找到trialId 了  
            if ( !string.IsNullOrWhiteSpace(trialIdStr))
            {

                //如果没缓存数据，可能定时任务没执行或者缓存丢失  在此重新缓存
                if (_provider.GetCount() == 0)
                {

                    var _trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository<Trial>)) as IRepository<Trial>;

                    var list = _trialRepository.IfNullThrowException().Select(t => new { TrialId = t.Id, TrialStatusStr = t.TrialStatusStr })
                      .ToList();

                    list.ForEach(t => _provider.Set(t.TrialId.ToString(), t.TrialStatusStr, TimeSpan.FromDays(7)));
                }

                var cacheResultDic = _provider.GetAll<string>(new[] { trialIdStr });

                var trialStatusStr = cacheResultDic[trialIdStr].Value;

                //意外 导致缓存过期，调整服务器时间，测试不想重启程序
                if (string.IsNullOrWhiteSpace(trialStatusStr))
                {
                    var trialRepository = context.HttpContext.RequestServices.GetService(typeof(IRepository<Trial>)) as IRepository<Trial>;
                    trialStatusStr = trialRepository?.Where(t => t.Id == Guid.Parse(trialIdStr)).Select(t => t.TrialStatusStr).FirstOrDefault();
                }

                //  这里是统一拦截 项目有关的操作允许情况(特殊的地方，比如项目配置（有的在多种状态（初始化，ongoing）都可以操作，有的仅仅在Initializing）还有  项目添加和更新，不走这里，特殊处理,不然在这里显得很乱，判断是哪个接口)
                if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t=>t== TrialOpt.BeforeOngoingCantOpt)  )
                {

                    await next.Invoke();

                }
                //  项目停止、或者完成  不允许操作
                else
                {
                    context.Result = new JsonResult(ResponseOutput.NotOk("本次请求被配置规则拦截：项目状态处于进行中状态时,才允许操作。"));

                }

            }
            //添加项目  签名系统文档的时候  不做拦截   但是更新项目  签名项目文档的时候需要拦截
            else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial ||t ==TrialOpt.SignSystemDocNoTrialId))
            {
                await next.Invoke();
            }

            else
            {
                //如果项目相关接口没有传递trialId 会来到这里,提醒，以便修改

                context.Result = new JsonResult(ResponseOutput.NotOk("该接口参数中，没有传递项目编号，请核对。"));
            }


        }
    }
}