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.authentication.CredentialsExpiredException;
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 ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;

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;

  private final JwtTokenService jwtTokenService;

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

  @Override
  public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,
                                              HttpServletResponse httpServletResponse)
      throws AuthenticationException {
    String tokenStr = extractAuthToken(httpServletRequest);
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
      authentication = new JwtAuthentication(null, null, tokenStr);
    }
    try {
      authentication = getAuthenticationManager().authenticate(authentication);
      if (!httpServletRequest.getRequestURI().endsWith("esia/logout")) {
        Token token = jwtTokenService.getToken(tokenStr);
        String[] ids = token.getUserAccountId().split(":");
        if (ids.length != 2) {
          throw new CredentialsExpiredException("Invalid token. User has no ervuId");
        }
      }
    }
    catch (CredentialsExpiredException e) {
      securityHelper.clearAccessCookies(httpServletResponse);
      httpServletResponse.setStatus(401);
      LOGGER.warn(e.getMessage());
      return null;
    }
    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);
  }
}
