package io.kaif.web.v1;

import java.util.Optional;

import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.rometools.utils.Strings;

import io.kaif.model.clientapp.ClientAppUserAccessToken;
import io.kaif.oauth.InsufficientScopeException;
import io.kaif.oauth.InvalidTokenException;
import io.kaif.oauth.MissingBearerTokenException;
import io.kaif.service.ClientAppService;

public class ClientAppUserAccessTokenArgumentResolver implements HandlerMethodArgumentResolver {

  private final ClientAppService clientAppService;

  public ClientAppUserAccessTokenArgumentResolver(ClientAppService clientAppService) {
    this.clientAppService = clientAppService;
  }

  @Override
  public boolean supportsParameter(MethodParameter parameter) {
    return parameter.getParameterType() == ClientAppUserAccessToken.class;
  }

  @Override
  public ClientAppUserAccessToken resolveArgument(MethodParameter parameter,
      ModelAndViewContainer mavContainer,
      NativeWebRequest webRequest,
      WebDataBinderFactory binderFactory) throws Exception {
    String tokenStr = Strings.trimToEmpty(webRequest.getHeader(HttpHeaders.AUTHORIZATION));
    if (!tokenStr.startsWith("Bearer ")) {
      throw new MissingBearerTokenException();
    }
    String token = tokenStr.substring("Bearer ".length(), tokenStr.length()).trim();
    Optional<ClientAppUserAccessToken> accessToken = clientAppService.verifyAccessToken(token);
    if (!accessToken.isPresent()) {
      throw new InvalidTokenException();
    }
    RequiredScope requiredScope = parameter.getMethodAnnotation(RequiredScope.class);
    if (!accessToken.get().containsScope(requiredScope.value())) {
      throw new InsufficientScopeException(requiredScope.value());
    }
    return accessToken.get();
  }
}