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;

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

            var notValidat = parm["cmd"].ToString();
            #region Token校验

            if (notValidat != "/api/Tenant/Login")
            {
                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();
            //string areaName = actionContext.ActionDescriptor.RouteValues["area"].ToString().ToLower();
            if (!actionName.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();//重置读取
            request.Body.Seek(0, SeekOrigin.Begin);
            request.Body.Position = 0;
            Stream stream = request.Body;
            Encoding encoding = Encoding.UTF8;
            if (request.ContentLength > 0)
            {
                byte[] buffer = new byte[request.ContentLength.Value];
                stream.Read(buffer, 0, buffer.Length);
                string responseData = Encoding.UTF8.GetString(buffer);
                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["userInfo"];
                }
                catch (SignatureVerificationException sve)
                {
                    string message = sve.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                         new ApiResult
                         {
                             resultCode = (int)ResultCode.TokenOverdue,
                             message = "用户凭证失效，请重新登录1",
                             data = null
                         });
                }
                catch (ArgumentException ae)
                {
                    string message = ae.Message;
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            resultCode = (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
                        {
                            resultCode = (int)ResultCode.TokenIllegal,
                            message = "用户凭证失效，请重新登录3",
                            data = null
                        });
                }
            }
            else
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenIllegal,
                            message = "用户凭证为空，请重新登录4",
                            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
                                     });
            }

        }
    }

}