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

import com.airlock.iam.base.api.domain.model.mapification.MapifiedValues;
import com.airlock.iam.common.api.application.dependency.injection.LegacyPluginInjectorFactory;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.Optional;

public final class PollingResultCache {
   public static final String NOT_ALLOWED_WHILE_POLLING = "Call is not allowed after the final polling result is ready.";
   public static final String POLLING_CACHE_KEY = "pollingCache";
   private static final String FINAL_RESULT_KEY = "final";
   private static final String QUERY_RESULT_KEY = "query";
   private static final ELogger LOG = ELoggerFactory.getLogger(PollingResultCache.class);
   private Optional<PollingResultCache.PollingResults> cache;

   public PollingResultCache() {
      this.cache = Optional.empty();
   }

   private PollingResultCache(PollingResultCache.PollingResults pollingResults) {
      this.cache = Optional.of(pollingResults);
   }

   public <T extends StepResult> void cacheResults(T result, T queryResult) {
      LOG.info("Storing final polling result in the step session and staying in step until final result is requested.");
      this.cache = Optional.of(new PollingResultCache.PollingResults(result, queryResult));
   }

   public boolean hasResult() {
      return this.cache.isPresent();
   }

   public <T extends StepResult> T getCachedResult(boolean queryOnly) {
      if (queryOnly) {
         LOG.debug("Final polling result is ready, returning stay result for query-only mode.");
         return (T)this.cache.orElseThrow().queryResult();
      } else {
         LOG.info("Returning final polling result from cache.");
         T result = (T)this.cache.orElseThrow().finalResult();
         this.cache = Optional.empty();
         return result;
      }
   }

   public Optional<MapifiedValues> mapify() {
      StepResultMapifier mapifier = stepResultMapifier();
      return this.cache
         .map(r -> new MapifiedValues(Map.of("final", mapifier.mapify(r.finalResult()).asMap(), "query", mapifier.mapify(r.queryResult()).asMap())));
   }

   public static PollingResultCache demapifyPollingCache(Optional<MapifiedValues> mapifiedValues) {
      StepResultMapifier mapifier = stepResultMapifier();
      return mapifiedValues.<PollingResultCache.PollingResults>map(
            v -> new PollingResultCache.PollingResults(mapifier.demapify(v.getValues("final")), mapifier.demapify(v.getValues("query")))
         )
         .map(PollingResultCache::new)
         .orElseGet(PollingResultCache::new);
   }

   private static StepResultMapifier stepResultMapifier() {
      return LegacyPluginInjectorFactory.getInjector().getInstance(StepResultMapifier.class);
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public boolean equals(Object o) {
      if (o == this) {
         return true;
      } else if (!(o instanceof PollingResultCache other)) {
         return false;
      } else {
         Object this$cache = this.cache;
         Object other$cache = other.cache;
         return this$cache == null ? other$cache == null : this$cache.equals(other$cache);
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public int hashCode() {
      int PRIME = 59;
      int result = 1;
      Object $cache = this.cache;
      return result * 59 + ($cache == null ? 43 : $cache.hashCode());
   }

   private record PollingResults(StepResult finalResult, StepResult queryResult) {
   }
}
