package com.airlock.iam.admin.infrastructure.service.rememberme;

import com.airlock.iam.admin.domain.model.rememberme.RememberMeTokenAdminRepository;
import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.user.UserId;
import com.airlock.iam.base.misc.exception.Exceptions;
import com.airlock.iam.core.infrastructure.db.JooqProvider;
import com.airlock.iam.database.infrastructure.JooqConfig;
import com.airlock.iam.database.infrastructure.JooqTransaction;
import com.airlock.iam.database.infrastructure.TenantId;
import com.airlock.iam.database.infrastructure.generated.schema.tables.RememberMeTokenTable;
import com.airlock.iam.database.infrastructure.generated.schema.tables.records.RememberMeTokenRecord;
import jakarta.inject.Inject;
import java.util.function.Supplier;
import lombok.NonNull;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.DeleteConditionStep;
import org.jooq.SelectConditionStep;

public final class RememberMeTokenAdminRepositoryImpl implements RememberMeTokenAdminRepository {
   @NonNull
   private final Supplier<DSLContext> jooqDslProvider;
   @NonNull
   private final TenantId tenantId;

   @Inject
   public RememberMeTokenAdminRepositoryImpl(@NonNull JooqConfig jooqConfig) {
      if (jooqConfig == null) {
         throw new NullPointerException("jooqConfig is marked non-null but is null");
      }

      this.jooqDslProvider = () -> JooqProvider.jooqProvider(jooqConfig).db();
      this.tenantId = jooqConfig.tenantId();
   }

   @Override
   public int deleteAllRememberMeTokensOf(UserId iamUserId) {
      return (Integer)Exceptions.wrapException(
         () -> (Integer)JooqTransaction.jooqTransaction(this.db()).executeAndReturn(db -> this.deleteAllRememberMeTokensOf(db, iamUserId).execute()),
         e -> new FailureException(String.format("Error while deleting Remember-Me tokens belonging to '%s'", iamUserId))
      );
   }

   private DeleteConditionStep<RememberMeTokenRecord> deleteAllRememberMeTokensOf(DSLContext db, UserId iamUserId) {
      return db.deleteFrom(RememberMeTokenTable.REMEMBER_ME_TOKEN)
         .where(RememberMeTokenTable.REMEMBER_ME_TOKEN.IAM_USER_ID.eq(iamUserId.getValue()))
         .and(this.tenantMatches());
   }

   private Condition tenantMatches() {
      return RememberMeTokenTable.REMEMBER_ME_TOKEN.TENANT_ID.eq(this.tenantId.tenantId());
   }

   @Override
   public boolean hasAnyRememberMeToken(UserId iamUserId) {
      return (Boolean)Exceptions.wrapException(() -> {
         int foundRecords = this.db().fetchCount(this.selectRememberMeTokensBelongingTo(this.db(), iamUserId));
         return foundRecords > 0;
      }, e -> new FailureException(String.format("Error while counting Remember-Me tokens belonging to '%s'", iamUserId), e));
   }

   private SelectConditionStep<RememberMeTokenRecord> selectRememberMeTokensBelongingTo(DSLContext db, UserId iamUserId) {
      return db.selectFrom(RememberMeTokenTable.REMEMBER_ME_TOKEN)
         .where(RememberMeTokenTable.REMEMBER_ME_TOKEN.IAM_USER_ID.eq(iamUserId.getValue()))
         .and(this.tenantMatches());
   }

   private DSLContext db() {
      return this.jooqDslProvider.get();
   }
}
