content: package com.seasonthon.everflow.app.global.security;
  
  import com.seasonthon.everflow.app.global.oauth.domain.CustomUserDetails;
  import com.seasonthon.everflow.app.user.domain.User;
  import com.seasonthon.everflow.app.user.repository.UserRepository;
  import jakarta.servlet.FilterChain;
  import jakarta.servlet.ServletException;
  import jakarta.servlet.http.HttpServletRequest;
  import jakarta.servlet.http.HttpServletResponse;
  import java.io.IOException;
  import java.util.Collections;
  import java.util.Optional;
  import lombok.RequiredArgsConstructor;
  import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  import org.springframework.security.core.Authentication;
  import org.springframework.security.core.authority.SimpleGrantedAuthority;
  import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
  import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
  import org.springframework.security.core.context.SecurityContextHolder;
  import org.springframework.web.filter.OncePerRequestFilter;
  
  @RequiredArgsConstructor
  public class JwtAuthenticationProcessingFilter extends OncePerRequestFilter {
  
      private final JwtService jwtService;
      private final UserRepository userRepository;
      private final TokenBlacklistService tokenBlacklistService;
      private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
  
      @Override
      protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
              throws ServletException, IOException {
  
          String uri = request.getRequestURI();
  
          if (uri.equals("/auth/login") || uri.equals("/api/notifications/subscribe")) {
              filterChain.doFilter(request, response);
              return;
          }
  
          String refreshToken = jwtService.extractRefreshToken(request)
                  .filter(jwtService::isTokenValid)
                  .orElse(null);
  
          if (refreshToken != null) {
              checkRefreshTokenAndReIssueAccessToken(response, refreshToken);
              return;
          }
  
          checkAccessTokenAndAuthentication(request, response, filterChain);
      }
  
      private void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) {
          userRepository.findByRefreshToken(refreshToken)
                  .ifPresent(user -> {
                      if (!user.isWithdrawn()) {
                          String reIssuedRefreshToken = reIssueRefreshToken(user);
                          jwtService.sendAccessAndRefreshToken(
                                  response,
                                  jwtService.createAccessToken(user.getEmail(), user.getId(), user.getRoleType().toString()),
                                  reIssuedRefreshToken
                          );
                      }
                  });
      }
  
      private String reIssueRefreshToken(User user) {
          String reIssuedRefreshToken = jwtService.createRefreshToken();
          user.updateRefreshToken(reIssuedRefreshToken);
          userRepository.saveAndFlush(user);
          return reIssuedRefreshToken;
      }
  
      private void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response,
                                                     FilterChain filterChain) throws ServletException, IOException {
          Optional<String> accessTokenOpt = jwtService.extractAccessToken(request);
  
          if (accessTokenOpt.isPresent()) {
              String accessToken = accessTokenOpt.get();
  
              if (tokenBlacklistService.isTokenBlacklisted(accessToken)) {
                  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                  return;
              }
  
              try {
                  String email = jwtService.verifyTokenAndGetEmail(accessToken);
                  userRepository.findByEmail(email)
                          .ifPresent(user -> {
                              if (!user.isWithdrawn()) {
                                  saveAuthentication(user);
                              }
                          });
              } catch (Exception e) {
                  response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                  return;
              }
          }
  
          filterChain.doFilter(request, response);
      }
  
      private void saveAuthentication(User myUser) {
          CustomUserDetails userDetailsUser = new CustomUserDetails(
                  Collections.singleton(new SimpleGrantedAuthority(myUser.getRoleType().toString())),
                  myUser.getEmail(),
                  myUser.getRoleType(),
                  myUser.getId());
  
          Authentication authentication = new UsernamePasswordAuthenticationToken(
                  userDetailsUser, null, authoritiesMapper.mapAuthorities(userDetailsUser.getAuthorities()));
  
          SecurityContextHolder.getContext().setAuthentication(authentication);
      }
  }
  