package com.airlock.iam.flow.shared.domain.model.valueprovider;

import com.airlock.iam.base.api.domain.model.exception.FailureException;
import com.airlock.iam.base.api.domain.model.plugin.LogicForConfig;
import com.airlock.iam.common.api.domain.model.valueprovider.ValueProvider;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.shared.api.domain.model.session.GenericStringsSessionAttribute;
import com.airlock.iam.flow.shared.api.domain.service.FlowAttributeService;
import com.airlock.iam.flow.shared.application.configuration.valueprovider.GenericSessionAttributeStringProviderConfig;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

@LogicForConfig(GenericSessionAttributeStringProviderConfig.class)
public final class GenericSessionAttributeStringProvider implements ValueProvider<String> {
   private static final ELogger LOG = ELoggerFactory.getLogger(GenericSessionAttributeStringProvider.class);
   private final Provider<String> key;
   private final Provider<Optional<String>> defaultValue;
   private final Provider<Boolean> failIfNull;
   private final Provider<FlowAttributeService> attributes;

   @Inject
   GenericSessionAttributeStringProvider(
      Provider<String> key, Provider<Optional<String>> defaultValue, Provider<Boolean> failIfNull, Provider<FlowAttributeService> attributes
   ) {
      this.key = key;
      this.defaultValue = defaultValue;
      this.failIfNull = failIfNull;
      this.attributes = attributes;
   }

   public String getValue() {
      String finalValue = ((FlowAttributeService)this.attributes.get())
         .findAttribute(GenericStringsSessionAttribute.class)
         .flatMap(attribute -> attribute.getValue((String)this.key.get()))
         .orElseGet(
            () -> ((Optional)this.defaultValue.get())
               .map(peek(value -> LOG.debug("Using configured default value for key \"" + (String)this.key.get() + "\" in value provider.")))
               .orElse(null)
         );
      if (finalValue == null && (Boolean)this.failIfNull.get()) {
         throw new FailureException("Value provider for key \"" + (String)this.key.get() + "\" returned null which is not allowed by configuration.");
      } else {
         return finalValue;
      }
   }

   private static <T> Function<T, T> peek(Consumer<T> consumer) {
      return t -> {
         consumer.accept(t);
         return t;
      };
   }
}
