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

import java.util.Collections;

import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;
import ru.micord.ervu.security.webbpm.jwt.model.Token;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;

@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {

  private final JwtTokenService jwtTokenService;

  @Autowired
  public JwtAuthenticationProvider(JwtTokenService jwtTokenService) {
    this.jwtTokenService = jwtTokenService;
  }

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    JwtAuthentication jwtAuthentication = (JwtAuthentication) authentication;
    String tokenStr = jwtAuthentication.getToken();

    Token token;
    try {
      token = jwtTokenService.getToken(tokenStr);
    }
    catch (ExpiredJwtException e) {
      throw new CredentialsExpiredException("The access token is expired and not valid anymore.",
          e
      );
    }
    catch (Exception e) {
      throw new BadCredentialsException("Authentication Failed.", e);
    }

    if (!jwtTokenService.isValid(token)) {
      throw new BadCredentialsException("Auth token is not valid for user " + token.getUserAccountId());
    }

    UsernamePasswordAuthenticationToken pwdToken =
        UsernamePasswordAuthenticationToken.authenticated(token.getUserAccountId(), null,
            Collections.emptyList()
        );

    return new JwtAuthentication(pwdToken, token.getUserAccountId(), token.getValue());
  }

  @Override
  public boolean supports(Class<?> aClass) {
    return JwtAuthentication.class.isAssignableFrom(aClass);
  }
}
