using JWT;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Mall.Common;
using Mall.Common.API;
using Mall.Common.Plugin;
using System;
using System.Net;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Mall.CacheManager.User;
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Features;
using System.Reflection;

namespace Mall.WebApi.Filter
{
    /// <summary>
    /// Api过滤属性
    /// </summary>
    public class ApiFilterAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// OnActionExecuting
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnActionExecuting(ActionExecutingContext actionContext)
        {
            string ip = actionContext.HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
            if (string.IsNullOrEmpty(ip))
            {
                ip = actionContext.HttpContext.Connection.RemoteIpAddress.ToString();
            }
            if (!string.IsNullOrEmpty(ip) && Common.BackListHelper.bankList.Contains(ip))
            {
                actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(new ApiResult
                {
                    resultCode = (int)ResultCode.FormRepeatSubmit,
                    message = "您已进入访问黑名单",
                    data = null
                });
                Common.Plugin.LogHelper.Write("OnActionExecuting:" + ip);
            }
 
            string token = "";
            #region api监控日志
            JObject 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

            //token
            var userToken = actionContext.HttpContext.Items[GlobalKey.TokenUserInfo];
            if (userToken != null && !string.IsNullOrEmpty(userToken.ToString()))
            {
                JObject parms = JObject.Parse(userToken.ToString());
                var requestFrom = parms.GetInt("requestFrom");
                var uid = parms.GetStringValue("uid");
                //后台用户
                if (requestFrom == 1)
                {
                    var cacheUser = UserReidsCache.GetUserLoginInfo(uid);
                    if (cacheUser == null || (cacheUser != null && (cacheUser.TenantId <= 0)))
                    {
                        actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenOverdue,
                            message = "Token验证失败!1",
                            data = null
                        });
                    }
                }
                else if(requestFrom==2)
                {
                    var cacheMiniAppUser = UserReidsCache.GetAppletUserLoginInfo(uid);
                    if (cacheMiniAppUser == null || (cacheMiniAppUser != null && (cacheMiniAppUser.UserId <= 0)))
                    {
                        actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(new ApiResult
                        {
                            resultCode = (int)ResultCode.TokenOverdue,
                            message = "Token验证失败!2",
                            data = null
                        });
                    }
                }
            }
            #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 验证表单重复提交
            var action = actionContext.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;
            string controllerName = action.RouteValues["controller"].ToString().ToLower();
            string actionName = action.RouteValues["action"].ToString().ToLower();
            if (!actionName.ToLower().Contains("get"))
            {
                var allowRepeat = action.MethodInfo.GetCustomAttribute(typeof(AllowRepeatAttribute));
                if (allowRepeat == null)
                {
                    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)
        {
            var request = actionContext.HttpContext.Request;
            JObject parm = Helper.ApiTokenHelper.GetRequestParameters(request, ref token);
            return parm;
        }

        /// <summary>
        /// token校验
        /// </summary>
        /// <param name="actionContext"></param>
        /// <param name="token"></param>
        private static void JWTValidat(ActionExecutingContext actionContext, string token)
        {
            //解析token，校验是否失效
            if (!string.IsNullOrEmpty(token))
            {
                try
                {
                    var userInfo= Helper.ApiTokenHelper.ParsingToken(token);
                    actionContext.HttpContext.Items[GlobalKey.TokenUserInfo] = JObject.Parse(Common.Plugin.JsonHelper.Serialize(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
                {
                    actionContext.Result = new Microsoft.AspNetCore.Mvc.JsonResult(
                        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获取用户私钥值
            //TODO查询用户秘钥
            string privateKey = "";
            //等封装了缓存再说

            #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
        }
    }
}