package com.airlock.iam.flow.shared.application.service.recovery;

import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.shared.api.domain.model.session.UserSessionScopedFlowAttribute;
import com.google.common.annotations.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;

final class SessionAttributeReader {
   private static final ELogger LOG = ELoggerFactory.getLogger(SessionAttributeReader.class);
   private static final String IGNORING = "Ignoring session attribute: ";
   private final Method creatorMethod;
   private final int index;
   private final BiConsumer<String, Exception> warnLoggerWithException;

   static SessionAttributeReader sessionAttributeReader(Method creatorMethod, int index) {
      return sessionAttributeReader(creatorMethod, index, (m, e) -> logWarn(m, e));
   }

   @VisibleForTesting
   static SessionAttributeReader sessionAttributeReader(Method creatorMethod, int index, BiConsumer<String, Exception> warnLoggerWithException) {
      return new SessionAttributeReader(creatorMethod, index, warnLoggerWithException);
   }

   private SessionAttributeReader(Method creatorMethod, int index, BiConsumer<String, Exception> warnLoggerWithException) {
      this.creatorMethod = creatorMethod;
      this.index = index;
      this.warnLoggerWithException = warnLoggerWithException;
   }

   Optional<UserSessionScopedFlowAttribute<?>> attribute(Map<String, String> rawAttributes) {
      try {
         return Optional.ofNullable((UserSessionScopedFlowAttribute<?>)this.creatorMethod.invoke(null, rawAttributes));
      } catch (Exception e) {
         this.warning("Ignoring session attribute: Could not create session attribute, the deserialization method had a problem: ", e);
         return Optional.empty();
      }
   }

   private void warning(String message, Exception e) {
      this.warnLoggerWithException.accept(message, e);
   }

   private static void logWarn(String message, Exception e) {
      LOG.warn(EDefaultLogType.GENERIC_LOG_TYPE, message, e);
   }

   @SuppressFBWarnings(justification = "generated code")
   public int index() {
      return this.index;
   }
}
