package com.airlock.iam.saml2.infrastructure.state;

import com.airlock.iam.common.domain.model.state.StateTimeout;
import com.airlock.iam.common.domain.service.state.StateRepository;
import com.airlock.iam.common.domain.service.state.StateRepositoryKey;
import com.airlock.iam.openfedlib.saml2.state.Saml2DataMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.annotation.Nullable;
import jakarta.inject.Provider;
import java.time.Duration;
import java.util.Optional;
import lombok.NonNull;

public final class StateStoreBackedSaml2DataMap<V> implements Saml2DataMap<V> {
   private static final StateTimeout DEFAULT_TIMEOUT = StateTimeout.timeoutWithIdleTimeout(Duration.ofHours(24L));
   private final Provider<StateRepository> stateStore;
   private final String mapName;
   private final Serializer<V> valueSerializer;

   @Override
   public void put(@NonNull String key, @Nullable V value) {
      if (key == null) {
         throw new NullPointerException("key is marked non-null but is null");
      }

      Optional.ofNullable(value)
         .map(this.valueSerializer::mapToStore)
         .ifPresent(serializedValue -> ((StateRepository)this.stateStore.get()).put(this.key(key), serializedValue, DEFAULT_TIMEOUT));
   }

   @Override
   public V get(@NonNull String key) {
      if (key == null) {
         throw new NullPointerException("key is marked non-null but is null");
      } else {
         return ((StateRepository)this.stateStore.get()).find(this.key(key)).value().map(this.valueSerializer::mapFromStore).orElse(null);
      }
   }

   @Override
   public boolean contains(@NonNull String key) {
      if (key == null) {
         throw new NullPointerException("key is marked non-null but is null");
      } else {
         return this.get(key) != null;
      }
   }

   @Override
   public V remove(@NonNull String key) {
      if (key == null) {
         throw new NullPointerException("key is marked non-null but is null");
      } else {
         return ((StateRepository)this.stateStore.get()).remove(this.key(key)).value().map(this.valueSerializer::mapFromStore).orElse(null);
      }
   }

   private StateRepositoryKey key(String key) {
      return new StateRepositoryKey(key, () -> this.mapName);
   }

   @SuppressFBWarnings(justification = "generated code")
   public StateStoreBackedSaml2DataMap(Provider<StateRepository> stateStore, String mapName, Serializer<V> valueSerializer) {
      this.stateStore = stateStore;
      this.mapName = mapName;
      this.valueSerializer = valueSerializer;
   }
}
