package com.airlock.iam.oauth2.infrastructure.validation;

import com.airlock.iam.base.misc.result.Result;
import com.airlock.iam.common.domain.model.authentication.factor.AuthenticationFactorConverter;
import com.airlock.iam.core.domain.model.authentication.factor.KnownAuthenticationFactorDetail;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.oauth2.api.domain.model.OAuth2ClientId;
import com.airlock.iam.oauth2.domain.model.credential.OAuth2AccessInfo;
import com.airlock.iam.oauth2.domain.model.token.RawOAuth2Token;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2ValidationResult;
import com.airlock.iam.oauth2.domain.model.validation.OAuth2ValidationStatus;
import com.airlock.iam.oauth2.domain.service.credential.OAuth2AccessTokenValidationService;
import com.airlock.iam.oauth2.domain.service.reporting.OAuth2ReportingService;
import com.airlock.iam.oauth2.infrastructure.credential.OAuth2AccessTokenExtractionService;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;

public final class OAuth2AccessTokenAccessValidator {
   private static final ELogger LOG = ELoggerFactory.getLogger(OAuth2AccessTokenAccessValidator.class);
   private final Provider<OAuth2AccessTokenExtractionService> accessTokenExtractionService;
   private final Provider<OAuth2AccessTokenValidationService> accessTokenValidator;
   private final OAuth2ReportingService reportingService;

   @Inject
   public OAuth2AccessTokenAccessValidator(
      Provider<OAuth2AccessTokenExtractionService> accessTokenExtractionService,
      Provider<OAuth2AccessTokenValidationService> accessTokenValidator,
      OAuth2ReportingService reportingService
   ) {
      this.accessTokenExtractionService = accessTokenExtractionService;
      this.accessTokenValidator = accessTokenValidator;
      this.reportingService = reportingService;
   }

   public Result<OAuth2AccessInfo, OAuth2ValidationResult> validateAccess(HttpServletRequest request) {
      Optional<RawOAuth2Token> rawToken = ((OAuth2AccessTokenExtractionService)this.accessTokenExtractionService.get()).extractAccessToken(request);
      if (rawToken.isEmpty()) {
         LOG.info("No access token found in request");
         this.reportingService.reportFactorUseFailure(AuthenticationFactorConverter.convertFactorDetail(KnownAuthenticationFactorDetail.TOKEN_OAUTH2));
         this.reportingService.reportLoginFailure();
         return Result.failure(new OAuth2ValidationResult(OAuth2ValidationStatus.BEARER_TOKEN_REQUIRED));
      } else {
         Result<OAuth2AccessInfo, OAuth2ValidationResult> accessInfo = ((OAuth2AccessTokenValidationService)this.accessTokenValidator.get())
            .validateAccessToken(rawToken.get());
         if (accessInfo.isFailure()) {
            this.reportingService.reportFactorUseFailure(AuthenticationFactorConverter.convertFactorDetail(KnownAuthenticationFactorDetail.TOKEN_OAUTH2));
            this.reportingService.reportLoginFailure();
            return accessInfo;
         } else {
            OAuth2ClientId clientId = accessInfo.get().getAccessToken().getClientId();
            this.reportingService
               .reportFactorUseSuccess(clientId, AuthenticationFactorConverter.convertFactorDetail(KnownAuthenticationFactorDetail.TOKEN_OAUTH2));
            this.reportingService.reportLoginSuccess();
            return accessInfo;
         }
      }
   }
}
