package com.airlock.iam.login.app.misc.oauth2.introspection;

import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.oauth2.api.application.configuration.claims.OAuth2UsernameTransformer;
import com.airlock.iam.oauth2.api.domain.model.OAuth2Scope;
import com.airlock.iam.oauth2.application.service.introspection.TokenIntrospectionResult;
import com.airlock.iam.oauth2.application.service.introspection.TokenIntrospectionService;
import com.airlock.iam.oauth2.domain.model.token.OAuth2AccessToken;
import com.airlock.iam.oauth2.domain.model.token.OAuth2Token;
import com.airlock.iam.oauth2.domain.model.token.introspection.OAuth2TokenIntrospectionTokenManager;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.time.Instant;
import java.util.Optional;

public final class TokenIntrospectionServiceImpl implements TokenIntrospectionService {
   private final OAuth2TokenIntrospectionTokenManager tokenManager;
   private final OAuth2LogMessageAppender logMessageGenerator;
   private final OAuth2UsernameTransformer usernameTransformer;
   private final ELogger logger;

   @Override
   public TokenIntrospectionResult introspect(String tokenValue) {
      Optional<OAuth2Token> token = this.tokenManager.getAccessOrRefreshTokenByTokenValue(tokenValue);
      if (token.isEmpty()) {
         this.logIntrospectionInformation("OAuth 2.0 Token Introspection: Requested token does not exist or is not valid.", tokenValue);
         return TokenIntrospectionResult.inactiveTokenResult().build();
      } else {
         Optional<PersistentUser> persistentUser = this.tokenManager.validUser(token.get());
         if (persistentUser.isEmpty()) {
            this.logIntrospectionInformation("OAuth 2.0 Token Introspection: Requested token not assigned to valid user.", tokenValue);
            return TokenIntrospectionResult.inactiveTokenResult().build();
         } else {
            this.logIntrospectionInformation("OAuth 2.0 Token Introspection: Returning successful token introspection result.", tokenValue);
            return this.getIntrospectionResult(token.get(), persistentUser.get());
         }
      }
   }

   private void logIntrospectionInformation(String message, String tokenValue) {
      this.logger.info(this.logMessageGenerator.appendInstanceAndTokenInfos(message, tokenValue));
   }

   private TokenIntrospectionResult getIntrospectionResult(OAuth2Token token, PersistentUser persistentUser) {
      TokenIntrospectionResult.ActiveTokenResultBuilder builder = TokenIntrospectionResult.activeTokenResult()
         .scopes(OAuth2Scope.convertToScopeNames(token.getScopes().scopesAsSet()))
         .clientId(token.getClientId().id())
         .subject(this.usernameTransformer.apply(persistentUser))
         .generationDate(token.getGenerationInstant().toEpochMilli());
      Optional<Instant> expiry = token.getExpiry();
      if (expiry.isPresent()) {
         builder.expiryDate(expiry.get().toEpochMilli());
      }

      if (token instanceof OAuth2AccessToken accessToken) {
         accessToken.getTokenBinding().ifPresent(builder::tokenBinding);
      }

      TokenIntrospectionResult result = builder.build();
      this.logger.debug("Returning introspection result " + result);
      return result;
   }

   @SuppressFBWarnings(justification = "generated code")
   public TokenIntrospectionServiceImpl(
      OAuth2TokenIntrospectionTokenManager tokenManager,
      OAuth2LogMessageAppender logMessageGenerator,
      OAuth2UsernameTransformer usernameTransformer,
      ELogger logger
   ) {
      this.tokenManager = tokenManager;
      this.logMessageGenerator = logMessageGenerator;
      this.usernameTransformer = usernameTransformer;
      this.logger = logger;
   }
}
