package com.airlock.iam.common.infrastructure.state.redis;

import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.application.configuration.state.ExpertModeRedisStateRepositoryConfig;
import com.airlock.iam.common.application.configuration.state.SingleModeRedisStateRepositoryConfig;
import com.airlock.iam.common.domain.model.state.FindResult;
import com.airlock.iam.common.domain.model.state.InsertAndUnlockAdditionalKeysResult;
import com.airlock.iam.common.domain.model.state.InsertResult;
import com.airlock.iam.common.domain.model.state.LockAndFindResult;
import com.airlock.iam.common.domain.model.state.LockResult;
import com.airlock.iam.common.domain.model.state.PutResult;
import com.airlock.iam.common.domain.model.state.RemoveAndUnlockResult;
import com.airlock.iam.common.domain.model.state.RemoveResult;
import com.airlock.iam.common.domain.model.state.StateLockId;
import com.airlock.iam.common.domain.model.state.StateTimeout;
import com.airlock.iam.common.domain.model.state.UpdateAndUnlockResult;
import com.airlock.iam.common.domain.service.state.StateRepository;
import com.airlock.iam.common.domain.service.state.StateRepositoryKey;
import com.airlock.iam.common.domain.service.state.StateRepositoryLockKey;
import com.google.common.annotations.VisibleForTesting;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.time.Duration;
import java.util.List;
import java.util.Optional;

@LogicForConfig({SingleModeRedisStateRepositoryConfig.class, ExpertModeRedisStateRepositoryConfig.class})
public final class RedisStateRepository implements StateRepository {
   public static final Duration DEFAULT_LOCK_LIFETIME = Duration.ofSeconds(12L);
   public static final Duration LOCK_LIFETIME_POLLING_INTERVAL = Duration.ofSeconds(5L);
   private final Provider<RedisCommandExecutor> redisCommandExecutor;
   private final Provider<StateLockId> stateLockId;
   private final Provider<StateEncryption> encryption;
   private final Provider<RedisStateRepositorySettings> settings;
   private final Provider<RedisLegacyKeyHelper> legacyKeyHelper;

   @Inject
   public RedisStateRepository(
      Provider<RedisCommandExecutor> redisCommandExecutor,
      Provider<StateLockId> stateLockId,
      Provider<StateEncryption> encryption,
      Provider<RedisStateRepositorySettings> settings,
      Provider<RedisLegacyKeyHelper> legacyKeyHelper
   ) {
      this.redisCommandExecutor = redisCommandExecutor;
      this.stateLockId = stateLockId;
      this.encryption = encryption;
      this.settings = settings;
      this.legacyKeyHelper = legacyKeyHelper;
   }

   @Override
   public FindResult find(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .find(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public Optional<String> findReadOnly(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .findReadOnly(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public InsertResult insert(StateRepositoryKey key, String value, StateTimeout timeout, StateRepositoryKey... additionalLockKeys) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .insert(
            key,
            value,
            timeout,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get(),
            additionalLockKeys
         );
   }

   @Override
   public PutResult put(StateRepositoryKey key, String value, StateTimeout timeout) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .put(
            key,
            value,
            timeout,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public RemoveResult remove(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .remove(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public LockAndFindResult lockAndFind(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .lockAndFind(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public InsertAndUnlockAdditionalKeysResult insertAndUnlockAdditionalKeys(
      StateRepositoryKey key, String value, StateTimeout timeout, StateRepositoryKey... additionalLockKeys
   ) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .insertAndUnlockAdditionalKeys(
            key,
            value,
            timeout,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get(),
            additionalLockKeys
         );
   }

   @Override
   public UpdateAndUnlockResult updateAndUnlock(StateRepositoryKey key, String value, StateTimeout timeout, StateRepositoryKey... newAdditionalKeys) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .updateAndUnlock(
            key,
            value,
            timeout,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get(),
            newAdditionalKeys
         );
   }

   @Override
   public RemoveAndUnlockResult removeAndUnlock(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .removeAndUnlock(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (StateEncryption)this.encryption.get(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public void removeAndForceUnlock(StateRepositoryKey key) {
      ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .removeAndForceUnlock(key, (RedisStateRepositorySettings)this.settings.get(), (RedisLegacyKeyHelper)this.legacyKeyHelper.get());
   }

   @Override
   public LockResult lock(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .lock(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public boolean unlock(StateRepositoryKey key) {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get())
         .unlock(
            key,
            ((StateLockId)this.stateLockId.get()).id(),
            (RedisStateRepositorySettings)this.settings.get(),
            (RedisLegacyKeyHelper)this.legacyKeyHelper.get()
         );
   }

   @Override
   public List<StateRepositoryLockKey> retrieveAllLockKeys() {
      return ((RedisCommandExecutor)this.redisCommandExecutor.get()).retrieveAllLockKeys((RedisStateRepositorySettings)this.settings.get());
   }

   @Override
   public void clear() {
      ((RedisCommandExecutor)this.redisCommandExecutor.get()).clear();
   }

   @VisibleForTesting
   public RedisCommandExecutor getRedisCommandExecutor() {
      return (RedisCommandExecutor)this.redisCommandExecutor.get();
   }
}
