package com.airlock.iam.flow.shared.domain.service;

import com.airlock.iam.flow.api.domain.model.step.GenericStepError;
import com.airlock.iam.flow.api.domain.model.step.StepResult;
import com.airlock.iam.flow.shared.domain.model.session.PollingResultCache;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.function.Function;
import java.util.function.Predicate;

public final class PollingService<S, R extends StepResult> {
   private static final String QUERY_RESULT_ATTRIBUTE = "finalResultAvailable";
   private final Function<S, PollingResultCache> cacheFromSession;
   private final Function<S, R> pollInternal;
   private final Function<S, R> createQueryResult;
   private final Predicate<R> pollingComplete;

   public PollingService(Function<S, PollingResultCache> cacheFromSession, Function<S, R> pollInternal, Function<S, R> createQueryResult) {
      this(cacheFromSession, pollInternal, createQueryResult, StepResult::isLeavingStep);
   }

   public R poll(S session, boolean queryOnly) {
      PollingResultCache cache = this.cacheFromSession.apply(session);
      if (cache.hasResult()) {
         return cache.getCachedResult(queryOnly);
      } else if (!queryOnly) {
         return this.pollInternal.apply(session);
      } else {
         R queryResult = this.createQueryResult.apply(session);
         R pollingResult = this.pollInternal.apply(session);
         if (pollingResult.hasError(GenericStepError.UNEXPECTED_CALL)) {
            return pollingResult;
         } else if (this.pollingComplete.test(pollingResult)) {
            queryResult = this.addFinalResultAttribute(queryResult, true);
            cache.cacheResults(pollingResult, queryResult);
            return queryResult;
         } else {
            return this.addFinalResultAttribute(pollingResult, false);
         }
      }
   }

   private R addFinalResultAttribute(R result, boolean finalResultAvailable) {
      return (R)result.toBuilder().addAdditionalAttribute("finalResultAvailable", finalResultAvailable).build();
   }

   @SuppressFBWarnings(justification = "generated code")
   public PollingService(
      Function<S, PollingResultCache> cacheFromSession, Function<S, R> pollInternal, Function<S, R> createQueryResult, Predicate<R> pollingComplete
   ) {
      this.cacheFromSession = cacheFromSession;
      this.pollInternal = pollInternal;
      this.createQueryResult = createQueryResult;
      this.pollingComplete = pollingComplete;
   }
}
