package ru.micord.ervu.security.webbpm.jwt.filter;

import java.io.IOException;
import java.lang.invoke.MethodHandles;
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.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import ru.micord.ervu.security.webbpm.jwt.JwtAuthentication;
import ru.micord.ervu.security.webbpm.jwt.helper.SecurityHelper;

import static ru.micord.ervu.security.webbpm.jwt.util.SecurityUtil.extractAuthToken;


/**
 * @author Flyur Karimov
 */
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  private static final Logger LOGGER = LoggerFactory.getLogger(
      MethodHandles.lookup().lookupClass());

  private final AuthenticationEntryPoint entryPoint;

  private final SecurityHelper securityHelper;

  public JwtAuthenticationFilter(RequestMatcher requestMatcher,
                                 AuthenticationEntryPoint entryPoint,
                                 SecurityHelper securityHelper) {
    super(requestMatcher);
    this.entryPoint = entryPoint;
    this.securityHelper = securityHelper;
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
                                              HttpServletResponse httpServletResponse)
      throws AuthenticationException {
    String browser = httpServletRequest.getHeader("User-Agent");

    if (browser == null || (!browser.contains("YaBrowser") && !browser.contains("Chromium GOST"))) {
      LOGGER.error("Invalid browser. Use YaBrowser or Chromium GOST");
      return clearCookieAndSetStatus(httpServletResponse);
    }
    String tokenStr = extractAuthToken(httpServletRequest);
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
      authentication = new JwtAuthentication(null, null, tokenStr);
    }
    try {
      authentication = getAuthenticationManager().authenticate(authentication);
    }
    catch (AuthenticationException e) {
      LOGGER.warn(e.getMessage());
      return clearCookieAndSetStatus(httpServletResponse);
    }
    return authentication;
  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                          FilterChain chain, Authentication authentication)
      throws IOException, ServletException {
    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(request, response);
  }

  @Override
  protected void unsuccessfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            AuthenticationException exception)
      throws IOException, ServletException {
    LOGGER.error("Jwt unsuccessful authentication exception", exception);
    SecurityContextHolder.clearContext();
    entryPoint.commence(request, response, exception);
  }

  private Authentication clearCookieAndSetStatus(HttpServletResponse httpServletResponse) {
    securityHelper.clearAccessCookies(httpServletResponse);
    httpServletResponse.setStatus(401);
    return null;
  }
}
