package ru.micord.ervu.account_applications.security.filter;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import ru.micord.ervu.account_applications.security.model.jwt.authentication.JwtTokenDummy;


@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
  private static final Logger LOGGER = LoggerFactory.getLogger(
      MethodHandles.lookup().lookupClass());

  private final AuthenticationManager authenticationManager;

  public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    try {
      Authentication authentication = attemptAuthentication(request);
      if (authentication != null) {
        SecurityContextHolder.getContext().setAuthentication(authentication);
      }
    }
    catch (AuthenticationException e) {
      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      LOGGER.warn(e.getMessage());
      return;
    }

    filterChain.doFilter(request, response);
  }

  protected Authentication attemptAuthentication(HttpServletRequest request)
      throws AuthenticationException {
    String token = extractAuthTokenFromRequest(request);
    if (token == null) {
      return null;
    }

    String permissionsHeader = extractPermissionsHeaderFromRequest(request);
    Authentication authentication = new JwtTokenDummy(token, permissionsHeader);
    return authenticationManager.authenticate(authentication);
  }

  private String extractAuthTokenFromRequest(HttpServletRequest request) {
    return Optional.ofNullable(request.getHeader("Authorization"))
        .map(token -> URLDecoder.decode(token, StandardCharsets.UTF_8))
        .map(token -> token.replace("Bearer", ""))
        .map(String::trim)
        .orElse(null);
  }

  private String extractPermissionsHeaderFromRequest(HttpServletRequest request) {
    return Optional.ofNullable(request.getHeader("X-User-Permissions"))
        .orElse("");
  }
}
