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

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.exception.ViolationException;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.exception.Exceptions;
import com.airlock.iam.base.misc.log.LogUtil;
import com.airlock.iam.core.infrastructure.db.JooqProvider;
import com.airlock.iam.database.infrastructure.DatabaseType;
import com.airlock.iam.database.infrastructure.JooqConfig;
import com.airlock.iam.database.infrastructure.JooqTransaction;
import com.airlock.iam.oauth2.domain.model.session.OAuth2GlobalSessionsRepository;
import com.airlock.iam.oauth2.domain.model.session.OperationStatistics;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;
import org.jooq.DSLContext;
import org.jooq.Sequence;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;

public final class OAuth2GlobalSessionsRepositoryImpl implements OAuth2GlobalSessionsRepository {
   private final Provider<OAuth2SessionRepositorySettings> settings;

   @Inject
   public OAuth2GlobalSessionsRepositoryImpl(Provider<OAuth2SessionRepositorySettings> settings) {
      this.settings = settings;
   }

   @Override
   public OperationStatistics deleteAllSessionsByUser(UserId userId) {
      return Exceptions.wrapException(
         () -> (OperationStatistics)JooqTransaction.jooqTransaction(this.db()).executeAndReturn(db -> this.getSessionUpdater(db).deleteOAuth2Sessions(userId)),
         e -> wrapOrForwardException(String.format("Error deleting OAuth 2.0 sessions for user %s", LogUtil.logSafeQuote(userId.getValue())), e)
      );
   }

   private OAuth2SessionUpdater getSessionUpdater(DSLContext db) {
      return OAuth2SessionUpdater.oAuth2SessionUpdater(db, this.getSequence(), ((OAuth2SessionRepositorySettings)this.settings.get()).usePersistedClaims());
   }

   private static RuntimeException wrapOrForwardException(String message, Exception originalException) {
      return originalException instanceof ViolationException ? (ViolationException)originalException : new FailureException(message, originalException);
   }

   private DSLContext db() {
      JooqConfig jooqConfig = ((OAuth2SessionRepositorySettings)this.settings.get()).jooqConfig();
      return JooqProvider.jooqProvider(jooqConfig).db();
   }

   private Optional<Sequence<Long>> getSequence() {
      return isOracleDatabase(((OAuth2SessionRepositorySettings)this.settings.get()).jooqConfig())
         ? Optional.of(DSL.sequence(DSL.name("oauth2_session_seq"), SQLDataType.BIGINT))
         : Optional.empty();
   }

   private static boolean isOracleDatabase(JooqConfig jooqConfig) {
      return jooqConfig.dbType().equals(DatabaseType.ORACLE);
   }
}
