package com.easyproject.core.interceptor;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.easyproject.common.constant.HttpStatus;
import com.easyproject.common.constant.SignConstants;
import com.easyproject.common.core.domain.AjaxClientResult;
import com.easyproject.common.utils.DateUtils;
import com.easyproject.common.utils.ServletUtils;
import com.easyproject.common.utils.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * <p>方法说明: 参数签名拦截器
 * <p>参数说明:
 * <p>返回说明:
 * <p>创建时间: 2025/2/13 0013 17:38
 * <p>创  建  人: JAVA —— java001
**/
@Slf4j
@Component
public class SignInterceptor implements HandlerInterceptor {

    /**
     * 2分钟有效期
     */
    private final static long MAX_EXPIRE = 5 * 60;

    // API接口签名密钥串（如：dd05f1c54d63749eda95f9fa6d49v442a）
    @Value("${project.signatureSecret}")
    private String signatureSecret;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        if (handler instanceof HandlerMethod) {

            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            log.info("获取到的请求方法 ===> "+ method.getName());

            //获取全部参数(包括URL和body上的)
            //签名
            SortedMap<String, String> params = new TreeMap<>();

            //获取post参数
            String jsonParam = new RequestWrapper(request).getBody();
            if(ObjectUtil.isNotEmpty(jsonParam)){
                Map mapObj = JSONObject.parseObject(jsonParam,Map.class);
                for (Object map: mapObj.entrySet()){
                    params.put((String)((Map.Entry)map).getKey(), ((Map.Entry)map).getValue()==null?"null":(String)((Map.Entry)map).getValue().toString());
                }
            }

            //获取get参数
            Enumeration<String> enu = request.getParameterNames();
            while (enu.hasMoreElements()) {
                String paramName = enu.nextElement().trim();
                params.put(paramName, URLDecoder.decode(request.getParameter(paramName), "UTF-8"));
            }

            log.info("获取到的参数 ===> "+ JSONUtil.toJsonStr(params));

            //获取请求头信息
            String headerSign = request.getHeader(SignConstants.H_SIGN);
            String hTimestamp = request.getHeader(SignConstants.H_TIMESTAMP);
            if(ObjectUtil.isEmpty(headerSign) || ObjectUtil.isEmpty(hTimestamp)){
                log.error("签名验证失败:H_SIGN、H_TIMESTAMP不能为空！");
                ServletUtils.renderString(response, JSON.toJSONString(AjaxClientResult.error(HttpStatus.SIGNING_FAILED, "签名验证失败:H_SIGN、H_TIMESTAMP不能为空！")));
                return false;
            }

            //万能密钥，调试使用
            if(headerSign.equals("D3176C1E7BE12505BD2B7487F8F64354")){
                return true;
            }

            //客户端时间
            Long clientTimestamp = Long.parseLong(hTimestamp);
            int length = 14;
            int length1000 = 1000;
            //1.校验签名时间（兼容X_TIMESTAMP的新老格式）
            if (hTimestamp.length() == length) {
                //a. X_TIMESTAMP格式是 yyyyMMddHHmmss (例子：20220308152143)
                if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
                    log.error("签名验证失败:H-TIMESTAMP已过期，注意系统时间和服务器时间是否有误差！");
                    ServletUtils.renderString(response, JSON.toJSONString(AjaxClientResult.error(HttpStatus.SIGNING_FAILED, "签名验证失败:H-TIMESTAMP已过期！")));
                    return false;
                }
            } else {
                //b. X_TIMESTAMP格式是 时间戳 (例子：1646552406000)
                if ((System.currentTimeMillis() - clientTimestamp) > (MAX_EXPIRE * length1000)) {
                    log.error("签名验证失败:H-TIMESTAMP已过期，注意系统时间和服务器时间是否有误差！");
                    ServletUtils.renderString(response, JSON.toJSONString(AjaxClientResult.error(HttpStatus.SIGNING_FAILED, "签名验证失败:H-TIMESTAMP已过期！")));
                    return false;
                }
            }

            //将header，中的时间加入到参数中作为签名参数
            params.put("_t",hTimestamp);

            //2.校验签名 所有参数排序后组成JSON，如 endJsonStr = {"isAsc":"desc","orderByColumn":"id","pageNum":"2","pageSize":"3"},
            // 签名对象为 endJsonStr + signatureSecret 取字节 getBytes ，再进行MD5加密获得Hex
            boolean isSigned = SignUtil.verifySign(params,headerSign,signatureSecret);

            if (!isSigned) {
                log.error("Sign 签名校验失败！获取到的签名 : {}",headerSign);
                ServletUtils.renderString(response, JSON.toJSONString(AjaxClientResult.error(HttpStatus.SIGNING_FAILED, "Sign 签名校验失败！")));
                return false;
            }

            log.debug("Sign 签名通过！获取到的签名 : {}",headerSign);

            return true;
        } else {
            return true;
        }
    }

}
