package com.ai.da.common.security.filter;

import cn.hutool.core.util.StrUtil;
import com.ai.da.common.config.exception.TokenMissingOrExpiredException;
import com.ai.da.common.context.UserContext;
import com.ai.da.common.security.config.SecurityProperties;
import com.ai.da.common.security.jwt.JWTTokenHelper;
import com.ai.da.common.utils.LocalCacheUtils;
import com.ai.da.common.utils.MultiReadHttpServletRequest;
import com.ai.da.common.utils.MultiReadHttpServletResponse;
import com.ai.da.common.utils.RequestInfoUtil;
import com.ai.da.model.vo.AuthPrincipalVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.security.sasl.AuthenticationException;
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.Arrays;
import java.util.List;

/**
 * @author: dangweijian
 * @description: 认证拦截器
 * @create: 2020-07-10 16:50
 **/
@Slf4j
@Configuration
public class AuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private JWTTokenHelper jwtTokenHelper;
    @Resource
    private SecurityProperties properties;

    private static final List<String> FILTER_URL =
            Arrays.asList("/favicon.ico", "/doc.html", "/api/account/login", "/api/account/preLogin", "api/account/sendEmail","api/account/noLoginRequired",
                    "/webjars/", "/swagger-resources", "/v2/api-docs", "/api/account/resetPwd",
                    "/api/python/saveGeneratePicture", "/api/python/getLibraryByUserId",
                    "/api/third/party/addUser","/api/third/party/addTrialUser", "/api/third/party/editUser", "/api/element/initDefaultSysFile",
                    "/api/third/party/addNoLoginRequiredNew","/api/third/party/deleteNoLoginRequiredNew","/api/third/party/updateNoLoginRequiredNew",
                    "/api/third/party/existNoLoginRequired","/api/third/party/getRedirectUrl",
                    "/api/python/flush","/api/account/healthy","/api/ali-pay/trade/notify","/api/paypal/ipn/back","/api/alipay-hk/trade/notify",
                    "/api/portfolio/page", "/api/portfolio/detail", "/api/portfolio/commentPage", "/api/portfolio/viewsIncrease",
                    "/api/account/designWorksRegister","/api/account/questionnaire","/api/stripe/trade/notify",
                    "/notification","/api/account/activateNewEmail","/api/third/party/auth/google_callback","/api/third/party/parseGoogleCredential","/api/third/party/receiveDesignResults","/api/third/party/parseWeChatCode","/api/third/party/receiveDesignParams"
                    , "/api/account/schoolLogin", "/api/account/enterpriseLogin", "/api/account/organizationNameSearch",
                    "/api/llm/stream"
            );

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, @NonNull HttpServletResponse httpServletResponse, @NonNull FilterChain filterChain) throws ServletException, IOException {
        String requestURI = httpServletRequest.getRequestURI();

        if (calculateUrl(requestURI)/* || hasAuthorizationToken(httpServletRequest)*/) {
            StopWatch stopWatch = new StopWatch();
            HttpServletRequest wrappedRequest = httpServletRequest;
            HttpServletResponse wrappedResponse = httpServletResponse;
            try {
                stopWatch.start();
                if ((httpServletRequest.getContentType() == null && httpServletRequest.getContentLength() > 0) || (httpServletRequest.getContentType() != null && !httpServletRequest.getContentType().contains("application/json"))) {
                    extracted(wrappedRequest);
                    filterChain.doFilter(wrappedRequest, wrappedResponse);
                } else {
                    wrappedRequest = new MultiReadHttpServletRequest(httpServletRequest);
                    wrappedResponse = new MultiReadHttpServletResponse(httpServletResponse);
                    extracted(wrappedRequest);
                    // excel导出使用原始response,不对响应做包装
                    if (requestURI.equals("/api/account/exportAccountsToExcel")) {
                        filterChain.doFilter(httpServletRequest, httpServletResponse); // 不包装
                    } else {
                        filterChain.doFilter(wrappedRequest, wrappedResponse);
                    }
                }
            } catch (Exception e) {
                SecurityContextHolder.clearContext();
                throw e;
            } finally {
                stopWatch.stop();
            }
        } else {
            //先清空当前线程变量,防止上一个线程遗留
            UserContext.delete();
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }

    private Boolean calculateUrl(String requestURI) {
        String filterUrl = FILTER_URL.stream().filter(url -> requestURI.contains(url)).findFirst().orElse(null);
        return null == filterUrl ? Boolean.TRUE : Boolean.FALSE;
    }

    private boolean hasAuthorizationToken(HttpServletRequest request) {
        String authorizationHeader = request.getHeader("Authorization");
        return authorizationHeader != null && authorizationHeader.startsWith("Bearer");
    }

    private void extracted(HttpServletRequest request) throws AuthenticationException {
        String jwtToken = request.getHeader(properties.getJwtTokenHeader());
//        log.debug("后台检查令牌:{}", jwtToken);

        if (StrUtil.isBlank(jwtToken)) {
            String ipAddress = RequestInfoUtil.getIpAddress(request);
            log.info("本次请求的ip为 ： " + ipAddress);
//            throw new RuntimeException("请传入token！");
            throw new TokenMissingOrExpiredException("请传入token！");
        }
        if(jwtToken.equals("Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiIyIiwic3ViIjoie1wiaWRcIjoyLFwidXNlcm5hbWVcIjpcImxpcnNcIn0iLCJpYXQiOjE2NjU3NDEwODcsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE2NzQzODEwODd9.ShM9R_NNFD7oo1OvxrEgg7PFeWinOuAKkuInUCMQupp66s64Hhv8tN0Wwr83nIN4rHPqtn95wmd4msWcvaFYJA")){
            //写死 暂时放行
            return;
        }
        // 检查token
        boolean validate = jwtTokenHelper.validateToken(jwtToken);
        if (validate) {
            AuthPrincipalVo principal = jwtTokenHelper.parserToUser(jwtToken);
            if (principal == null) {
//                throw new RuntimeException("TOKEN已过期，请重新登录！");
                throw new TokenMissingOrExpiredException("TOKEN已过期，请重新登录！(token without userInfo)");
            }
            //先清空当前线程变量,防止上一个线程遗留
            UserContext.delete();
            //存取用户信息到缓存
            UserContext.setUserHolder(principal);
            //校验token
            String cacheToken = LocalCacheUtils.getTokenCache(String.valueOf(principal.getId()));

            if(StringUtils.isEmpty(cacheToken)){
//                throw new RuntimeException("TOKEN已过期，请重新登录！");
                throw new TokenMissingOrExpiredException("TOKEN已过期，请重新登录！(local cache empty)");
            }
            if(!cacheToken.equals(jwtToken) ){
//                throw new RuntimeException("TOKEN已过期，请重新登录！");
                throw new TokenMissingOrExpiredException("TOKEN已过期，请重新登录！(token not match local cache)");
            }
//                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(null, null);
//                SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    }
}
