package com.liquidnet.service.dragon.config;

import com.liquidnet.common.cache.redis.util.RedisUtil;
import com.liquidnet.commons.lang.core.JwtValidator;
import com.liquidnet.commons.lang.util.CurrentUtil;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.service.base.ErrorMapping;
import com.liquidnet.service.base.ResponseDto;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.DigestUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "access-filter")
public class AccessHandlerIntercept extends HandlerInterceptorAdapter {
    private List<String> includeUrlPattern;
    private List<String> excludeUrlPattern;

    private static final String CONTENT_TYPE = "application/json;charset=utf-8";
    private static final String TOKEN_ILLEGAL = "40001";
    private static final String TOKEN_KICK = "40002";
    private static final String TOKEN_INVALID = "40003";

//    private static final String KYLIN_STATION_JWT_VALID = "/*/station/**";

    private final static AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    Environment env;
    @Autowired
    JwtValidator jwtValidator;
    @Autowired
    RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String authorization = request.getHeader(CurrentUtil.uToken), uri = request.getRequestURI(),
                responseCode = null, token = null, currentUid = null;

        Claims claims = null;
        if (StringUtils.isNotBlank(authorization) && StringUtils.length(authorization) > 7) {
            token = authorization.substring(7);
            try {
                // 解析没有异常则表示token验证通过，如有必要可根据自身需求增加验证逻辑
                claims = jwtValidator.parse(token);

                currentUid = (String) claims.get(CurrentUtil.TOKEN_SUB);

                request.setAttribute(CurrentUtil.uToken, token);
                request.setAttribute(CurrentUtil.TOKEN_SUB, currentUid);
                request.setAttribute(CurrentUtil.uTag, JsonUtils.toJson(claims));

            } catch (ExpiredJwtException expiredJwtEx) {
                responseCode = TOKEN_INVALID;
                log.error("Ex.ExpiredJwtException:{},responseCode:{}", expiredJwtEx.getMessage(), responseCode);
            } catch (Exception ex) {
                responseCode = TOKEN_ILLEGAL;
                log.error("Ex.Exception:{},responseCode:{}", ex.getMessage(), responseCode);
            }
        } else {
            responseCode = TOKEN_ILLEGAL;
        }

        for (String urlPattern : excludeUrlPattern) {
            if (antPathMatcher.match(urlPattern, uri)) {
                return true;
            }
        }
        if (StringUtils.isNotEmpty(responseCode)) {
            this.responseHandler(response, responseCode);
            log.warn("Authority failed:{},uri:[{}],authorization:{}", responseCode, uri, authorization);
            return false;
        }
        if (StringUtils.isEmpty(currentUid)) {
            this.responseHandler(response, TOKEN_ILLEGAL);
            return false;
        }
        if (this.authorityHandler(response, uri, token, currentUid, claims)) {
            return true;
        }
        return false;
    }

    private void responseHandler(HttpServletResponse response, String responseCode) throws IOException {
        ResponseDto<Object> responseDto = ResponseDto.failure(ErrorMapping.get(responseCode));
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType(CONTENT_TYPE);
        response.getWriter().write(JsonUtils.toJson(responseDto));
    }

    private boolean authorityHandler(HttpServletResponse response, String uri, String token, String currentUid, Claims claims) throws IOException {
//        if (antPathMatcher.match(KYLIN_STATION_JWT_VALID, uri)) {// 专业版APP
//            // adam:identity:sso:${uid}:MD5(${token})=${1-在线|0-离线}
//            String ssoUidM5TokenKey = jwtValidator.getMsoRedisKey()
//                    .concat(currentUid).concat(":").concat(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)));
//            Integer online = (Integer) redisUtil.get(ssoUidM5TokenKey);
//            if (null == online || online != 1) {
//                // 已离线
//                this.responseHandler(response, TOKEN_INVALID);
//
//                return false;
//            } else {
//                return true;
//            }
//        } else {
//            // adam:identity:sso:${uid}=MD5(${token})
//            String ssoKey = jwtValidator.getSsoRedisKey().concat(currentUid), md5Token;
//
//            if (StringUtils.isEmpty(md5Token = (String) redisUtil.get(ssoKey))) {
//                // 已离线
//                this.responseHandler(response, TOKEN_INVALID);
//
//                return false;
//            } else {
//                // 与在线TOKEN比对
//                if (md5Token.equals(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)))) {
//                    // 一致则放行
//                    return true;
//                } else {
//                    // 不一致则被踢下线
//                    this.responseHandler(response, TOKEN_KICK);
//
//                    return false;
//                }
//            }
//        }


        String tokenType = (String) claims.get(CurrentUtil.TOKEN_TYPE);
        switch (tokenType) {
            case CurrentUtil.TOKEN_TYPE_VAL_STATION:// 专业版APP
                // adam:identity:sso:${uid}:MD5(${token})=${1-在线|0-离线}
                String ssoUidM5TokenKey = jwtValidator.getMsoRedisKey()
                        .concat(currentUid).concat(":").concat(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)));
                Integer online = (Integer) redisUtil.get(ssoUidM5TokenKey);
                if (null == online || online != 1) {
                    // 已离线
                    this.responseHandler(response, TOKEN_INVALID);

                    return false;
                } else {
                    return true;
                }
            case CurrentUtil.TOKEN_TYPE_VAL_USER:
                // adam:identity:sso:${uid}=MD5(${token})
                String ssoKey = jwtValidator.getSsoRedisKey().concat(currentUid), md5Token;

                if (StringUtils.isEmpty(md5Token = (String) redisUtil.get(ssoKey))) {
                    // 已离线
                    this.responseHandler(response, TOKEN_INVALID);

                    return false;
                } else {
                    // 与在线TOKEN比对
                    if (md5Token.equals(DigestUtils.md5DigestAsHex(token.getBytes(StandardCharsets.UTF_8)))) {
                        // 一致则放行
                        return true;
                    } else {
                        // 不一致则被踢下线
                        this.responseHandler(response, TOKEN_KICK);

                        return false;
                    }
                }
            default:
                log.warn("Authority failed:{} (Unknown token type).uri:[{}],token:{}", TOKEN_ILLEGAL, uri, token);
                this.responseHandler(response, TOKEN_ILLEGAL);
                return false;
        }
    }
}
