using JWT;
using JWT.Serializers;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Mall.Common;
using Mall.Common.API;
using Mall.Common.Plugin;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Configuration;
using Mall.Common.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Mall.CacheManager.User;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;

namespace Mall.WebApi.Filter
{
    /// <summary>
    /// Api过滤属性
    /// </summary>
    public class ApiFilterAttribute : ActionFilterAttribute
    {
        //private readonly string Key = "_ApiOnActionMonitorLog_";

        /// <summary>
        /// OnActionExecuting
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            //请求参数
            JObject parm = new JObject();
            string token = "";
            #region api监控日志
            parm = DoApiMonitorLog(actionContext, ref token);
            #endregion
            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 签名校验权限校验
            if (actionContext.HttpContext.Items[GlobalKey.TokenUserInfo] != null)
            {
                string openValidation = new ConfigurationBuilder().Add(new JsonConfigurationSource { Path = "appsettings.json" }).Build().GetSection("OpenValidation").Value;
                if (openValidation.Equals("True"))
                {
                    TokenUserInfo userInfo = JsonConvert.DeserializeObject<TokenUserInfo>(actionContext.HttpContext.Items[GlobalKey.TokenUserInfo].ToString());
                    if (userInfo != null && (userInfo.requestFrom == Mall.Common.Enum.ApiRequestFromEnum.Web || userInfo.requestFrom == Mall.Common.Enum.ApiRequestFromEnum.MiniProgram))
                    {
                        SignValidat(actionContext, parm);
                    }
                    else
                    {
                        #region 权限校验

                        if (userInfo.uid != Config.AdminId)
                        {

                            SignValidat(actionContext, parm);
                            //PermissionValidat(actionContext);
                        }
                        #endregion
                    }
                }
            }
            #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
                        {
                            resultCode = (int)ResultCode.FormRepeatSubmit,
                            message = "表单重复提交，请稍后再试",
                            data = null
                        });
                    }
                    else
                    {
                        //默认3秒钟之内不能重复提交
                        UserReidsCache.Set(cachedKey, 1, 3);
                    }
                }
                catch
                {
                }
            }
            #endregion

        }

        /// <summary>
        /// OnActionExecuted
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
        {

        }

        /// <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();
                // Do some processing with body…
                // Reset the request body stream position so the next middleware can read it
                responseData = body.Result;
                request.Body.Position = 0;
            }
            if (!string.IsNullOrWhiteSpace(responseData.Trim()))
            {
                try
                {
                    parm = JObject.Parse(responseData);
                    actionContext.HttpContext.Items[GlobalKey.UserPostInfo] = responseData;
                }
                catch (Exception ex)
                {
                    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 = Config.JwtSecretKey;
                    var json = decoder.Decode(token, secret, verify: true);//token为之前生成的字符串
                    JObject jwtJson = JObject.Parse(json);
                    actionContext.HttpContext.Items[GlobalKey.TokenUserInfo] = jwtJson["mall_userInfo"];
                    //TokenUserInfo userInfo = JsonConvert.DeserializeObject<TokenUserInfo>(muserInfo.ToString());
                    //if (userInfo != null && userInfo.requestFrom == Common.Enum.ApiRequestFromEnum.MiniProgram)
                    //{
                    //    //查询是否是黑名单
                    //    AppletUserInfo uInfo = UserReidsCache.GetAppletUserBlacklistInfo(userInfo.uid);
                    //    if ((uInfo?.Blacklist ?? 0) == 1)
                    //    {
                    //        actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                    //        new ApiResult
                    //        {
                    //            resultCode = (int)ResultCode.TokenIllegal,
                    //            message = "已进入黑名单，无法访问",
                    //            data = null
                    //        });
                    //    }
                    //}
                }
                catch (SignatureVerificationException sve)
                {
                    string message = sve.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                         new ApiResult
                         {
                             resultCode = (int)ResultCode.TokenOverdue,
                             message = "用户凭证失效，请重新登录",
                             data = null
                         });
                }
                catch (ArgumentException ae)
                {
                    string message = ae.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenIllegal,
                            message = "用户凭证无效，请重新登录",
                            data = null
                        });
                }
                catch (Exception ex)
                {
                    string message = ex.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        HttpStatusCode.OK,
                        new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenIllegal,
                            message = "用户凭证失效，请重新登录",
                            data = null
                        });
                }
            }
            else
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenIllegal,
                            message = "用户凭证为空，请重新登录",
                            data = null
                        });
            }
        }

        /// <summary>
        /// 签名校验
        /// </summary>
        /// <param name="actionContext"></param>
        /// <param name="parm"></param>
        private static void SignValidat(ActionExecutingContext actionContext, JObject parm)
        {
            JObject jMsg = JObject.Parse(parm["msg"].ToString());
            string msg = JsonConvert.SerializeObject(jMsg);
            string timestamp = JsonHelper.GetStringValue(parm, "timestamp");
            string token = JsonHelper.GetStringValue(parm, "token");
            string sign = JsonHelper.GetStringValue(parm, "sign");

            #region 判断请求是否过期
            DateTime requestTime = StringHelper.GetDateTimeByTicks(timestamp);

            if (requestTime.AddSeconds(Config.ApiExpirTime) < DateTime.Now)
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                                     new ApiResult
                                     {
                                         resultCode = (int)ResultCode.RequestOverdue,
                                         message = "请求过期",
                                         data = null
                                     });
            }
            #endregion

            #region 根据uid获取用户私钥值
            TokenUserInfo tokenUserInfo = JsonConvert.DeserializeObject<TokenUserInfo>(actionContext.HttpContext.Items[GlobalKey.TokenUserInfo].ToString());
            //TODO查询用户秘钥
            string privateKey = "";
            //等封装了缓存再说

           // UserInfo userInfo = UserReidsCache.GetUserLoginInfo(tokenUserInfo.uid);
            //if (userInfo != null)
            //{
            //    privateKey = userInfo.SecretKey;
            //}

            #endregion

            #region 校验签名是否合法
            string str = string.Format("msg={0}&timestamp={1}&token={2}&key={3}", StringHelper.UrlEncode(msg).ToLower().Replace("+", "%20"), timestamp, token, privateKey);
            string currentSign = SecurityHelper.MD5(str);
            if (sign != currentSign)
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                                     new ApiResult
                                     {
                                         resultCode = (int)ResultCode.SignIllegal,
                                         message = "签名不合法",
                                         data = null
                                     });
            }
            #endregion
        }

        /// <summary>
        /// 权限校验
        /// </summary>
        /// <param name="actionContext"></param>
        private static void PermissionValidat(ActionExecutingContext actionContext)
        {
            string uid = actionContext.HttpContext.Items[GlobalKey.TokenUserInfo] != null ? JsonConvert.DeserializeObject<TokenUserInfo>(actionContext.HttpContext.Items[GlobalKey.TokenUserInfo].ToString()).uid : "0";
            //TODO查询用户权限
            string url = actionContext.HttpContext.Request.GetAbsoluteUri();
            bool havPermission = true;

            //List<object> args = new List<object>() {
            //    uid
            //};

            //根据uid 判断用户是否拥有该权限
            //Type type = (Type)_obj;
            //MethodInfo meth = type.GetMethod(model.Method);
            //try
            //{
            //    permission = (string)meth.Invoke(Activator.CreateInstance(type), args.ToArray());
            //}
            //catch
            //{
            //}
            //if (!string.IsNullOrWhiteSpace(permission))
            //{
            //    if (permission.ToLower().Contains(url.ToLower()))
            //    {
            //        havPermission = true;
            //    }
            //    else//判断菜单是否存在
            //    {
            //        bool systemIsExit = RbUserCache.GetSystemHasMenu(url.ToLower());
            //        if (!systemIsExit)
            //        {
            //            havPermission = true;
            //        }
            //    }
            //}



            if (havPermission == false)
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                                     new ApiResult
                                     {
                                         resultCode = (int)ResultCode.NoPermission,
                                         message = "权限不足",
                                         data = null
                                     });
            }

        }
    }

}