using JWT;
using JWT.Serializers;
using Newtonsoft.Json.Linq;
using System;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using Edu.Common.Plugin;
using Edu.Common.API;
using Edu.Cache.User;

namespace Edu.WebApi.Filter
{
    /// <summary>
    /// Api过滤属性
    /// </summary>
    public class ApiFilterAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// OnActionExecuting
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            string token = "";
            //请求参数
            JObject parm = DoApiMonitorLog(actionContext, ref token);
            bool isCheckToken = true;
            var endpoint = actionContext.HttpContext.Features.Get<IEndpointFeature>()?.Endpoint;
            if (endpoint != null && endpoint.Metadata.GetMetadata<AllowAnonymousAttribute>() != null)
            {
                isCheckToken = false;
            }

            #region Token校验

            if (isCheckToken)
            {
                JWTValidat(actionContext, token);
            }
            
            #endregion

            #region 验证表单重复提交
            
            string controllerName = actionContext.ActionDescriptor.RouteValues["controller"].ToString().ToLower();
            string actionName = actionContext.ActionDescriptor.RouteValues["action"].ToString().ToLower();
            if (!actionName.ToLower().Contains("get"))
            {
                string cachedKey = SecurityHelper.MD5(string.Format("cmd={0}&token={1}", controllerName + "/" + actionName, token));
                try
                {
                    if (UserReidsCache.Exists(cachedKey))//判断表单是否重复提交
                    {
                        actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(new ApiResult
                        {
                            Code = (int)ResultCode.FormRepeatSubmit,
                            Message = "表单重复提交，请稍后再试",
                            Data = null
                        });
                    }
                    else
                    {
                        //默认2秒钟之内不能重复提交
                        UserReidsCache.Set(cachedKey, 1, 2);
                    }
                }
                catch
                {
                }
            }
            #endregion
        }


        /// <summary>
        /// 解析post参数
        /// </summary>
        /// <param name="actionContext"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        private JObject DoApiMonitorLog(ActionExecutingContext actionContext, ref string token)
        {
            JObject parm = new JObject();
            var request = actionContext.HttpContext.Request;
            #region 如果参数是json实体对象，获取序列化后的数据
            request.EnableBuffering();
            string responseData = "";
            using (var reader = new StreamReader(request.Body, encoding: Encoding.UTF8))
            {
                var body = reader.ReadToEndAsync();
                responseData = body.Result;
                request.Body.Position = 0;
            }
            if (!string.IsNullOrWhiteSpace(responseData.Trim()))
            {
                try
                {
                    parm = JObject.Parse(responseData);
                    actionContext.HttpContext.Items[Common.GlobalKey.UserPostInfo] = responseData;
                }
                catch (Exception ex)
                {
                   Common.Plugin.LogHelper.Write(ex, string.Format("DoApiMonitorLog:{0}", responseData));
                }
                token = JsonHelper.GetStringValue(parm, "token");
            }
            #endregion
            return parm;
        }

        /// <summary>
        /// token校验
        /// </summary>
        /// <param name="actionContext"></param>
        /// <param name="token"></param>
        private static void JWTValidat(ActionExecutingContext actionContext, string token)
        {
            if (!string.IsNullOrEmpty(token))
            {
                //解析token，校验是否失效
                try
                {
                    IJsonSerializer serializer = new JsonNetSerializer();
                    IDateTimeProvider provider = new UtcDateTimeProvider();
                    IJwtValidator validator = new JwtValidator(serializer, provider);
                    IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
                    IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
                    string secret =Common.Config.JwtSecretKey;
                    var json = decoder.Decode(token, secret, verify: true);//token为之前生成的字符串
                    JObject jwtJson = JObject.Parse(json);
                    actionContext.HttpContext.Items[Common.GlobalKey.TokenUserInfo] = jwtJson["edu_userInfo"];
                }
                catch (SignatureVerificationException sve)
                {
                    string message = sve.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                         new ApiResult
                         {
                             Code = (int)ResultCode.TokenOverdue,
                             Message = "用户凭证失效，请重新登录1",
                             Data = null
                         });
                }
                catch (ArgumentException ae)
                {
                    string message = ae.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            Code = (int)ResultCode.TokenIllegal,
                            Message = "用户凭证无效，请重新登录2",
                            Data = null
                        });
                }
                catch (Exception ex)
                {
                    string message = ex.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        HttpStatusCode.OK,
                        new ApiResult
                        {
                            Code = (int)ResultCode.TokenIllegal,
                            Message = "用户凭证失效，请重新登录3",
                            Data = null
                        });
                }
            }
            else
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            Code = (int)ResultCode.TokenIllegal,
                            Message = "用户凭证为空，请重新登录4",
                            Data = null
                        });
            }
        }
    }
}