package com.dgtly.apiframework.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.dgtly.common.annotation.ApiNoCheckSign;
import com.dgtly.common.core.domain.AjaxResult;
import com.dgtly.common.core.domain.ResultType;
import com.dgtly.common.utils.security.Md5Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 接口加密验证拦截器
 */
@Component
public class VerifyInterceptor  extends HandlerInterceptorAdapter {

    protected final Logger logger = LoggerFactory.getLogger(VerifyInterceptor.class);

    @Value("#{${security.platMap}}")
    private Map<String,String> platMap;

    @Value("#{${security.isDev}}")
    private Boolean isDev;

    @Value("${security.apiTimeOut}")
    private int apiTimeOut;


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        crossDomain(request, response);
        if (handler instanceof HandlerMethod) {
            HandlerMethod h =(HandlerMethod)handler;
            //只对项目中的controller进行拦截 翻过boot的处理方法
            String handlerPackage = h.getBeanType().getPackage().getName();
            String thisPackage = this.getClass().getPackage().getName();
            String verifPackage =thisPackage.substring(0,thisPackage.indexOf(".",thisPackage.indexOf(".")+1));
            if(handlerPackage.indexOf(verifPackage)!=-1){
                ApiNoCheckSign a1=((HandlerMethod)handler).getBeanType().getAnnotation(ApiNoCheckSign.class);
                ApiNoCheckSign a2 = ((HandlerMethod)handler).getMethodAnnotation(ApiNoCheckSign.class);
                /**
                 * 判断类或方法上是否有放行注解  只要一个有就不验证
                 */
                if(a1==null&&a2==null){
                    if(isDev){
                        logger.info("开发模式 测试 验签放行");
                        return true;
                    }
                    /**
                     * 验证是否存在timestamp
                     */
                    String timestamp = request.getHeader("timestamp");
                    if(timestamp==null||timestamp.trim().equals("")){
                        returnError(response,ResultType.ERRORSIGN);
                        return false;
                    }
                    /**
                     * 超时验证
                     */
                    long now = new Date().getTime();
                    long time =Long.parseLong(timestamp);
                    if(Math.abs(time-now)>1000*60*apiTimeOut){
                        returnError(response,ResultType.TIMEOUT);
                        return false;
                    }
                    /**
                     * 验证是否存在plat
                     */
                    String plat = request.getHeader("plat");
                    if(plat==null||plat.trim().equals("")){
                        returnError(response,ResultType.ERRORSIGN);
                        return false;
                    }
                    /**
                     * 验证是否存在plat是否有对应的appkey
                     */
                    if(platMap==null&&!platMap.containsKey(plat)){
                        returnError(response,ResultType.ERRORSIGN);
                        return false;
                    }
                    String appKey =platMap.get(plat);
                    /**
                     * 验证是否存在v
                     */
                    String v = request.getHeader("v");
                    if(v==null||v.trim().equals("")){
                        returnError(response,ResultType.ERRORSIGN);
                        return false;
                    }
                    /**
                     * 验证是否存在sign
                     */
                    String sign = request.getHeader("sign");
                    if(sign==null||sign.trim().equals("")){
                        returnError(response,ResultType.ERRORSIGN);
                        return false;
                    }
                    /**
                     * 按照规则拼接字符串并加密比对
                     */
                    String mySign = "timestamp"+timestamp+"plat"+plat+"v"+v+"appKey"+appKey;
                    mySign = Md5Utils.hash(mySign);
                    if(!mySign.equalsIgnoreCase(sign)){
                        returnError(response,ResultType.SIGNERROR);
                        return false;
                    }
                }
                logger.info("======收到请求======"+((HandlerMethod) handler).getClass().getName()+"类的"+
                        ((HandlerMethod) handler).getMethod().getName()+"方法");
            }
            return true;
        }else
        {
            return super.preHandle(request, response, handler);
        }
    }

    public void returnError(HttpServletResponse response, ResultType type) throws IOException {

        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = null ;
        try{

            AjaxResult result = AjaxResult.error(type);
            out = response.getWriter();
            out.append(JSONObject.toJSONString(result));
        }
        catch (Exception e){
            e.printStackTrace();
            response.sendError(500);
        }

    }

    /**
     *跨域请求头拦截
     * @param request
     * @param response
     */
    public boolean crossDomain(HttpServletRequest request, HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept," +
                "token,sign,v,plat,timestamp,appKey");
        response.setHeader("Access-Control-Allow-Credentials","true");
        response.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
        String method= request.getMethod();
        if (method.equals("OPTIONS")){
            response.setStatus(200);
            return false;
        }
        return true;
    }

}
