package com.gyoomi.adam.core.filter;

import com.gyoomi.adam.core.BaseController;
import com.gyoomi.adam.core.CHERRY;
import com.gyoomi.adam.core.jwt.JwtTokenUtils;
import com.gyoomi.adam.core.jwt.UserDetailsServiceImpl;
import com.gyoomi.adam.core.model.Response;
import com.gyoomi.adam.core.properties.AdamProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * token拦截器
 *
 * @author Leon
 * @version 2019/10/21 20:58
 */
public class TokenFilter extends OncePerRequestFilter {

    private static final Logger lg = LoggerFactory.getLogger(TokenFilter.class);


    /**
     * Same contract as for {@code doFilter}, but guaranteed to be
     * just invoked once per request within a single request thread.
     * See {@link #shouldNotFilterAsyncDispatch()} for details.
     * <p>Provides HttpServletRequest and HttpServletResponse arguments instead of the
     * default ServletRequest and ServletResponse ones.
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (!isExclusives(request)) {
            AdamProperties adamProperties = CHERRY.SPRING_CONTEXT.getBean(AdamProperties.class);
            String jwtToken = request.getHeader(adamProperties.getSecurity().getJwtToken().getHeaderName());
            if (StringUtils.isBlank(jwtToken)) {
                jwtToken = request.getParameter(adamProperties.getSecurity().getJwtToken().getRequestName());
            }

            if (StringUtils.isNotBlank(jwtToken) && checkTokenStyle(jwtToken) && checkToken(jwtToken)) {
                filterChain.doFilter(request, response);
            } else {
                lg.warn("token参数为空或失效:{}", jwtToken);
                Response responseResult = Response.builder().code(HttpServletResponse.SC_NOT_IMPLEMENTED).msg("Token参数为空或失效").build();
                BaseController.writeJsonResponse(responseResult, response);
            }
        } else {
            filterChain.doFilter(request, response);
        }
    }

    private boolean checkToken(String token) {
        boolean valid = false;
        String loginNameFromToken = JwtTokenUtils.getLoginNameFromToken(token);
        // TODO
        if (null != loginNameFromToken && null == SecurityContextHolder.getContext().getAuthentication()) {
            UserDetails userDetails = CHERRY.SPRING_CONTEXT.getBean(UserDetailsServiceImpl.class).loadUserByUsername(loginNameFromToken);
            if (JwtTokenUtils.valid(token, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
                valid = true;
            }
        }
        return valid;
    }

    /**
     * Check the formatter of token
     *
     * @param token the token needed to be valid
     * @return result
     */
    private boolean checkTokenStyle(String token) {
        AdamProperties adamProperties = CHERRY.SPRING_CONTEXT.getBean(AdamProperties.class);
        String prefix = adamProperties.getSecurity().getJwtToken().getPrefix() + " ";
        return StringUtils.startsWith(token, prefix);
    }

    private boolean isExclusives(HttpServletRequest request) {
        List<String> exclusivePath = CHERRY.SPRING_CONTEXT.getBean(AdamProperties.class).getSecurity().getExclusivePath();
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        String requestURI = request.getRequestURI();
        for (String exclusive : exclusivePath) {
            if (antPathMatcher.match(exclusive, requestURI)) {
                return true;
            }
        }
        return false;
    }
}