package com.airlock.iam.servicecontainer.app.infrastructure.continuation;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.infrastructure.db.DbInstantConverter;
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.FlowContinuationTokenTable;
import com.airlock.iam.servicecontainer.app.domain.model.task.continuation.FlowContinuationTokenCleanupRepository;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Supplier;
import org.jooq.Condition;
import org.jooq.DSLContext;

public final class FlowContinuationTokenCleanupRepositoryImpl implements FlowContinuationTokenCleanupRepository {
   private final Supplier<DSLContext> jooqDslProvider;
   private final TenantId tenantId;
   private final Duration keepExpiredTokensDuration;

   public FlowContinuationTokenCleanupRepositoryImpl(JooqConfig jooqConfig, Duration keepExpiredTokensDuration) {
      this.jooqDslProvider = () -> JooqProvider.jooqProvider(jooqConfig).db();
      this.tenantId = jooqConfig.tenantId();
      this.keepExpiredTokensDuration = keepExpiredTokensDuration;
   }

   @Override
   public int deleteExpiredTokens(int batchSize) {
      return (Integer)Exceptions.wrapException(
         () -> (Integer)JooqTransaction.jooqTransaction(this.db())
            .executeAndReturn(
               db -> db.deleteFrom(FlowContinuationTokenTable.FLOW_CONTINUATION_TOKEN)
                  .where(this.tokenIsExpired())
                  .and(this.tenantMatches())
                  .limit(batchSize)
                  .execute()
            ),
         e -> new FailureException(String.format("Error deleting expired flow continuation tokens: %s", e.getMessage()), e)
      );
   }

   @Override
   public int getExpiredTokensCount() {
      return (Integer)Exceptions.wrapException(
         () -> this.db().fetchCount(FlowContinuationTokenTable.FLOW_CONTINUATION_TOKEN, this.tokenIsExpired().and(this.tenantMatches())),
         e -> new FailureException(String.format("Error fetching expired flow continuation token count: %s", e.getMessage()), e)
      );
   }

   private Condition tenantMatches() {
      return FlowContinuationTokenTable.FLOW_CONTINUATION_TOKEN.TENANT_ID.eq(this.tenantId.tenantId());
   }

   private Condition tokenIsExpired() {
      long deleteOn = DbInstantConverter.toNanoSecondsSinceEpoch(Instant.now().minusSeconds(this.keepExpiredTokensDuration.toSeconds()));
      return FlowContinuationTokenTable.FLOW_CONTINUATION_TOKEN.VALID_UNTIL.lessThan(deleteOn);
   }

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