package com.energy.manage.service.filter;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.energy.manage.common.constant.redis.ManagerRedisKeyConstant;
import com.energy.manage.common.exception.TokenInvalidException;
import com.energy.manage.common.exception.UserNotLoginException;
import com.energy.manage.service.config.annotations.PassToken;
import com.energy.manage.service.filter.threadLocal.ThreadContext;
import com.energy.manage.service.mappers.system.SysUserRoleMapper;
import com.energy.manage.service.service.system.SysUserRoleService;
import com.energy.manage.service.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Slf4j
@Component
public class LoginVerifyInterceptor implements HandlerInterceptor {


    private Logger logger = LoggerFactory.getLogger(LoginVerifyInterceptor.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${spring.profiles.active}")
    private String env;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {

        ThreadContext.setThreadNote(UUID.randomUUID().toString().replace("-", ""));

        long begin = System.currentTimeMillis();
        request.setAttribute("begin", begin);

        String requestURI = request.getRequestURI();
        String methodType = request.getMethod();

        // 从 http 请求头中取出 token
        String token = request.getHeader("token");
        log.info("token === >" + token);

        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释，有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        // 执行认证
        if (StringUtils.isEmpty(token)) {
//            //如果dev环境并且 token为空,那么可以请求,否则会进行验证
//            if ("dev".equalsIgnoreCase(env)) {
//                if ("dev".equalsIgnoreCase(env)) {
//                    LoginUser devUser = new LoginUser();
//                    devUser.setUserId(-1);
//                    LoginUserContextHolder.set(devUser);
//                    return true;
//                }
//                request.setAttribute("tokenUserId", 9999999L);
//                return true;
//            }

            logger.info("========token不能为null========");
            throw new TokenInvalidException();
        }
        // 获取 token 中的 user 信息
        Map<String, String> userMap = null;
        try {
            userMap = JwtUtil.getJwtMap(token);
            LoginUser devUser = new LoginUser();
            if(ObjectUtils.isEmpty(userMap.get("userName"))){
                throw new TokenInvalidException();
            }
            Integer userId = 0;
            if(userMap.get("userId")!=null){
                devUser.setUserId(Integer.valueOf(userMap.get("userId")));
            }
            devUser.setUserPhone(userMap.get("userPhone"));
            devUser.setUserName(userMap.get("userName"));
            if(userMap.get("userRoleId")!=null){
                devUser.setCurrentRoleId(Integer.valueOf(userMap.get("userRoleId")));
            }
            LoginUserContextHolder.set(devUser);
        } catch (JWTDecodeException j) {
            logger.error(String.format("token：%s  ========> token异常，访问系统被拦截", token));
            throw new TokenInvalidException();
        }
        log.info("userMap >>> " + userMap.size() + " userName " + userMap.get("userName"));
        String rediskey = String.format(ManagerRedisKeyConstant.SYSTEM_USER_LOGIN_INFO, userMap.get("userName"));
        Optional<Object> userOrgJson = Optional.ofNullable(stringRedisTemplate.opsForValue().get(rediskey));
        if (!userOrgJson.isPresent()) {
            logger.warn(String.format("用户：%s token失效，访问系统被拦截", userMap.get("userName")));
            throw new TokenInvalidException();
        }
        // 验证 token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(userMap.get("userPassword"))).build();
        try {
            jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            logger.warn(String.format("用户：%s token验证失败，访问系统被拦截", userMap.get("userId")));
            throw new UserNotLoginException();
        }
        request.setAttribute("tokenUserId", userMap.get("userId"));
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest req, HttpServletResponse resp, Object arg2, Exception e) throws Exception {
        Long begin = (Long) req.getAttribute("begin");
        if (null == begin) {
            return;
        }
        long end = System.currentTimeMillis();
        long timeDiff = end - begin;
        String uri = req.getRequestURI() + "---" + getParamValue(req.getParameterMap());
//        logger.info(String.format("method:%s*****time:%sms*****", uri, timeDiff));
        ThreadContext.clearMdc();
    }


    @Override
    public void postHandle(HttpServletRequest req, HttpServletResponse resp, Object o, ModelAndView modelAndView) throws Exception {
//        ThreadPoolExecutor
//        Collections.synchronizedList()

    }

    private String getParamValue(Map<String, String[]> map) {
        StringBuilder builder = new StringBuilder();
        for (String key : map.keySet()) {
            builder.append(key).append(":");
            String[] args = map.get(key);
            if (args != null && args.length > 0) {
                builder.append(args[0]).append(";");
            }
        }
        return String.valueOf(builder);
    }


    /**
     * 判断本次请求的数据类型是否为json
     */
    private boolean isJson(HttpServletRequest request) {
        if (request.getContentType() != null) {
            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) ||
                    request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE);
        }
        return false;
    }


}
