package cn.datax.service.data.market.api.mapping.handler;

import cn.datax.common.core.DataConstant;
import cn.datax.common.core.R;
import cn.datax.common.utils.IPUtil;
import cn.datax.common.utils.MD5Util;
import cn.datax.service.data.market.api.dto.ApiLogDto;
import cn.datax.service.data.market.api.entity.DataApiEntity;
import cn.datax.service.data.market.api.enums.ParamType;
import cn.datax.service.data.market.api.mapping.utils.ThreadUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RequestInterceptor {

    private RedisTemplate<String, Object> redisTemplate;

    public RequestInterceptor(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 请求之前执行
     *
     * @return 当返回对象时，直接将此对象返回到页面，返回null时，继续执行后续操作
     * @throws Exception
     */
    public Object preHandle(HttpServletRequest request, HttpServletResponse response, DataApiEntity api, Map<String, Object> params) throws Exception {
        System.out.println("************ApiInterceptor preHandle executed**********");
        String uri = request.getRequestURI();
        log.info("getRequestURI的值：" + uri);
        String ipAddr = IPUtil.getIpAddr(request);
        log.info("ipAddr的值：" + ipAddr);

        // 记录日志
        ApiLogDto log = new ApiLogDto();
        log.setCallerIp(ipAddr);
        log.setCallerUrl(uri);
        log.setCallerParams(new ObjectMapper().writeValueAsString(params));
        log.setCallerDate(LocalDateTime.now());
        log.setStatus(DataConstant.EnableState.ENABLE.getKey());
        ThreadUtil.getInstance().set(log);

        // 密钥校验
        String apiKey = request.getHeader("api_key");
        String secretKey = request.getHeader("secret_key");
        if (StrUtil.isBlank(apiKey) || StrUtil.isBlank(secretKey)) {
            ThreadUtil.getInstance().get().setStatus(DataConstant.EnableState.DISABLE.getKey());
            ThreadUtil.getInstance().get().setMsg("api_key或secret_key空");
            return R.error("api_key或secret_key空");
        }
        MD5Util mt = MD5Util.getInstance();
        String apiId = mt.decode(apiKey);
        String userId = mt.decode(secretKey);

        ThreadUtil.getInstance().get().setApiId(apiId);
        ThreadUtil.getInstance().get().setCallerId(userId);

        // 黑名单校验
        String deny = api.getDeny();
        if (StrUtil.isNotBlank(deny)) {
            List<String> denyList = Arrays.asList(deny.split(","));
            if (CollUtil.isNotEmpty(denyList)) {
                for (String ip : denyList) {
                    if(ip.equals(ipAddr)){
                        ThreadUtil.getInstance().get().setStatus(DataConstant.EnableState.DISABLE.getKey());
                        ThreadUtil.getInstance().get().setMsg("此IP已被加入API调用黑名单");
                        return R.error("此IP已被加入API调用黑名单");
                    }
                }
            }
        }
        // 参数校验
        if (MapUtil.isNotEmpty(params)) {
            try {
                api.getReqParams().stream().forEach(param -> {
                    if (params.containsKey(param.getParamName())) {
                        // 参数类型是否正确
                        ParamType.parse(ParamType.getParamType(param.getParamType()), params.get(param.getParamName()));
                    }
                });
            } catch (Exception e) {
                ThreadUtil.getInstance().get().setStatus(DataConstant.EnableState.DISABLE.getKey());
                ThreadUtil.getInstance().get().setMsg(e.getMessage());
                return R.error(e.getMessage());
            }
        }
        // 限流校验
        String apiName = api.getApiName();

        ThreadUtil.getInstance().get().setApiName(apiName);

        String rateLimit = api.getRateLimit();
        rateLimit = Optional.ofNullable(rateLimit).orElse(DataConstant.TrueOrFalse.TRUE.getKey());
        if (DataConstant.TrueOrFalse.TRUE.getKey().equals(rateLimit)) {
            Integer times = api.getTimes();
            Integer seconds = api.getSeconds();
            // 请求次数
            times = Optional.ofNullable(times).orElse(5);
            // 请求时间范围60秒
            seconds = Optional.ofNullable(seconds).orElse(60);
            // 根据 USER + API 限流
            String key = "user:" + userId + ":api:" + apiId;
            // 根据key获取已请求次数
            Integer maxTimes = (Integer) redisTemplate.opsForValue().get(key);
            if (maxTimes == null) {
                // set时一定要加过期时间
                redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS);
            } else if (maxTimes < times) {
                redisTemplate.opsForValue().set(key, maxTimes + 1, seconds, TimeUnit.SECONDS);
            } else {
                // 请求过于频繁
                ThreadUtil.getInstance().get().setStatus(DataConstant.EnableState.DISABLE.getKey());
                ThreadUtil.getInstance().get().setMsg("API调用过于频繁");
                return R.error("API调用过于频繁");
            }
        }
        return null;
    }

    /**
     * 执行完毕之后执行
     *
     * @throws Exception
     */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, DataApiEntity api, Map<String, Object> params, Object value) throws Exception {
    }
}
