package cn.datax.service.data.market.api.call.config;

import cn.datax.common.core.DataConstant;
import cn.datax.common.core.R;
import cn.datax.common.utils.HttpUtil;
import cn.datax.common.utils.IPUtil;
import cn.datax.common.utils.MD5Util;
import cn.datax.common.utils.ResponseUtil;
import cn.datax.service.data.market.api.call.service.ApiLogService;
import cn.datax.service.data.market.api.call.utils.ThreadUtil;
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.DataType;
import cn.datax.service.data.market.api.feign.DataApiServiceFeign;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

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 ApiInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private DataApiServiceFeign dataApiServiceFeign;

    @Autowired
    private ApiLogService apiLogService;

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 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);
        String apiKey = request.getHeader("api_key");
        String secretKey = request.getHeader("secret_key");
        if (StrUtil.isBlank(apiKey) || StrUtil.isBlank(secretKey)) {
            ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                    HttpServletResponse.SC_FORBIDDEN, R.error("api_key或secret_key空"));
            return false;
        }
        MD5Util mt = MD5Util.getInstance();
        String apiId = mt.decode(apiKey);
        String userId = mt.decode(secretKey);
        R apiResult = dataApiServiceFeign.getDataApiById(apiId);
        if (apiResult == null || !apiResult.isSuccess() || ObjectUtil.isEmpty(apiResult.getData())) {
            ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                    HttpServletResponse.SC_FORBIDDEN, R.error("查询API{" + apiId + "}出错"));
            return false;
        }
        DataApiEntity dataApiEntity = null;
        try {
            dataApiEntity = objectMapper.readValue(objectMapper.writeValueAsString(apiResult.getData()), DataApiEntity.class);
        } catch (JsonProcessingException e) {
        }
        if (dataApiEntity == null) {
            ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                    HttpServletResponse.SC_FORBIDDEN, R.error("API不存在"));
            return false;
        }
        if (!request.getMethod().equals(dataApiEntity.getReqMethod())){
            ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                    HttpServletResponse.SC_FORBIDDEN, R.error("API请求类型不对"));
            return false;
        }
        String deny = dataApiEntity.getDeny();
        deny = Optional.ofNullable(deny).orElse("");
        List<String> denyList = Arrays.asList(deny.split(","));
        if (CollUtil.isNotEmpty(denyList)) {
            for (String ip : denyList) {
                if(ip.equals(ipAddr)){
                    ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                            HttpServletResponse.SC_FORBIDDEN, R.error("IP已被加入API调用黑名单"));
                    return false;
                }
            }
        }
        // 接收参数
        String bodyString = HttpUtil.getBodyString(request);
        Map<String, Object> params = null;
        try {
            params = objectMapper.readValue(bodyString, Map.class);
        } catch (JsonProcessingException e) {
        }
        Map<String, Object> finalParams = params;
        dataApiEntity.getReqParams().stream().forEach(param -> {
            if (finalParams.containsKey(param.getParamName())) {
                Integer dataType = param.getDataType();
                // 数据类型是否正确
                DataType.parse(DataType.getDataType(dataType), finalParams.get(param.getParamName()));
            }
        });
        String apiName = dataApiEntity.getApiName();
        Integer rateLimit = dataApiEntity.getRateLimit();
        rateLimit = Optional.ofNullable(rateLimit).orElse(DataConstant.TrueOrFalse.TRUE.getKey());
        // 限流
        if (DataConstant.TrueOrFalse.TRUE.getKey() == rateLimit) {
            Integer times = dataApiEntity.getTimes();
            Integer seconds = dataApiEntity.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 {
                // 请求过于频繁
                ResponseUtil.makeResponse(response, MediaType.APPLICATION_JSON_VALUE,
                        HttpServletResponse.SC_FORBIDDEN, R.error("API调用过于频繁"));
                return false;
            }
        }
        ApiLogDto log = new ApiLogDto();
        log.setApiId(apiId);
        log.setApiName(apiName);
        log.setCallerId(userId);
        log.setCallerIp(ipAddr);
        log.setCallerUrl(uri);
        log.setCallerParams(bodyString);
        log.setCallerDate(LocalDateTime.now());
        log.setTime(System.currentTimeMillis());
        log.setStatus(DataConstant.TrueOrFalse.TRUE.getKey());
        ThreadUtil.getInstance().set(log);
        return true;
    }

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

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadUtil.getInstance().get().setTime(System.currentTimeMillis() - ThreadUtil.getInstance().get().getTime());
        log.info("调用耗时：" + ThreadUtil.getInstance().get().getTime());
        log.info("调用日志：" + ThreadUtil.getInstance().get());
        apiLogService.saveApiLog(ThreadUtil.getInstance().get());
        ThreadUtil.getInstance().remove();
    }
}
