package com.iformall.interceptor;

import com.iformall.common.ErrorCode;
import com.iformall.exception.MallinkException;
import com.iformall.utils.HashUtil;
import com.iformall.utils.IPUtil;
import com.iformall.utils.UrlCheck;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import redis.clients.jedis.Protocol;
import redis.clients.util.SafeEncoder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;

/**
 * 幂等检查
 * @author stormeye.wu
 * @email wuguoqiang@iformall.com
 * @date 2017-03-23 15:38
 */
@Component
public class RequestInterceptor extends HandlerInterceptorAdapter {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ("GET".equalsIgnoreCase(request.getMethod())) {
            // 获取不检查幂等
            return true;
        }

        String ipaddress = IPUtil.getIpAddr(request);
        String url = request.getRequestURL().toString();
        if (UrlCheck.checkUrl(url)) {
            // pvlog不检查幂等
            // awsFileUpload不检查幂等
            // ueditor 不检查幂等
            return true;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(url);
        sb.append("method=").append(request.getMethod()).append("&");
        sb.append("ip=").append(ipaddress).append("&");

        final Enumeration parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String key = (String) parameterNames.nextElement();
            if(key.equalsIgnoreCase("ran")) // 跳过ran
                continue;
            String parameter = request.getParameter(key);
            sb.append(key).append("=").append(parameter).append("&");
        }

        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        String resultBody = new String(outSteam.toByteArray(), Charset.forName("UTF-8"));
        inStream.close();
        outSteam.close();

        sb.append(resultBody);

        String key = "request:A:" + HashUtil.md5(sb.toString());
        Boolean isAbsent = redisTemplate.<Boolean>execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
                RedisSerializer keySerializer = redisTemplate.getKeySerializer();
                Object obj = connection.execute("set", keySerializer.serialize(key),
                        valueSerializer.serialize(key),
                        SafeEncoder.encode("NX"),
                        SafeEncoder.encode("EX"),
                        Protocol.toByteArray(3)); // 3s
                return obj != null;
            }
        });
        if (isAbsent) {
            logger.info(key + ": 第一次提交");
            return true;
        }
        logger.info(key + ": 第二次提交");
        throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION);
    }

    @Override
    public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
