package com.njcn.rdms.framework.security.core.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.njcn.rdms.framework.common.biz.system.oauth2.OAuth2TokenCommonApi;
import com.njcn.rdms.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO;
import com.njcn.rdms.framework.common.exception.ServiceException;
import com.njcn.rdms.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.util.json.JsonUtils;
import com.njcn.rdms.framework.common.util.servlet.ServletUtils;
import com.njcn.rdms.framework.security.config.SecurityProperties;
import com.njcn.rdms.framework.security.core.LoginUser;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.framework.web.core.handler.GlobalExceptionHandler;
import com.njcn.rdms.framework.web.core.util.WebFrameworkUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

@RequiredArgsConstructor
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {

    private final SecurityProperties securityProperties;
    private final GlobalExceptionHandler globalExceptionHandler;
    private final OAuth2TokenCommonApi oauth2TokenApi;

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        LoginUser loginUser = buildLoginUserByHeader(request);

        if (loginUser == null) {
            String token = SecurityFrameworkUtils.obtainAuthorization(request,
                    securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
            if (StrUtil.isNotEmpty(token)) {
                Integer userType = WebFrameworkUtils.getLoginUserType(request);
                try {
                    loginUser = buildLoginUserByToken(token, userType);
                    if (loginUser == null) {
                        loginUser = mockLoginUser(token, userType);
                    }
                } catch (Throwable ex) {
                    CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex);
                    ServletUtils.writeJSON(response, result);
                    return;
                }
            }
        }

        if (loginUser != null) {
            SecurityFrameworkUtils.setLoginUser(loginUser, request);
        }
        chain.doFilter(request, response);
    }

    private LoginUser buildLoginUserByToken(String token, Integer userType) {
        try {
            OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token).getCheckedData();
            if (accessToken == null) {
                return null;
            }
            if (userType != null && ObjectUtil.notEqual(accessToken.getUserType(), userType)) {
                throw new AccessDeniedException("错误的用户类型");
            }
            return new LoginUser().setId(accessToken.getUserId())
                    .setUserType(accessToken.getUserType())
                    .setInfo(accessToken.getUserInfo())
                    .setScopes(accessToken.getScopes())
                    .setExpiresTime(accessToken.getExpiresTime());
        } catch (ServiceException serviceException) {
            if (ObjectUtil.equal(serviceException.getCode(), GlobalErrorCodeConstants.UNAUTHORIZED.getCode())) {
                return null;
            }
            throw serviceException;
        }
    }

    private LoginUser mockLoginUser(String token, Integer userType) {
        if (!securityProperties.getMockEnable()) {
            return null;
        }
        if (!token.startsWith(securityProperties.getMockSecret())) {
            return null;
        }
        Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
        return new LoginUser().setId(userId).setUserType(userType);
    }

    private LoginUser buildLoginUserByHeader(HttpServletRequest request) {
        String loginUserStr = request.getHeader(SecurityFrameworkUtils.LOGIN_USER_HEADER);
        if (StrUtil.isEmpty(loginUserStr)) {
            return null;
        }
        try {
            loginUserStr = URLDecoder.decode(loginUserStr, StandardCharsets.UTF_8);
            LoginUser loginUser = JsonUtils.parseObject(loginUserStr, LoginUser.class);
            Integer userType = WebFrameworkUtils.getLoginUserType(request);
            if (userType != null && loginUser != null && ObjectUtil.notEqual(loginUser.getUserType(), userType)) {
                throw new AccessDeniedException("错误的用户类型");
            }
            return loginUser;
        } catch (Exception ex) {
            log.error("[buildLoginUserByHeader][parse LoginUser({}) error]", loginUserStr, ex);
            throw ex;
        }
    }

}
