package com.airlock.iam.common.domain.model.async;

import com.airlock.iam.common.api.domain.model.async.AsynchronousExecutor;
import com.airlock.iam.elog.api.EDefaultLogType;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public final class AsynchronousExecutorImpl implements AsynchronousExecutor {
   private static final ELogger LOG = ELoggerFactory.getLogger(AsynchronousExecutorImpl.class);
   private static final AsynchronousExecutorImpl INSTANCE = new AsynchronousExecutorImpl();
   private ExecutorService executor = createExecutorService();

   private static ExecutorService createExecutorService() {
      return Executors.newCachedThreadPool();
   }

   public static AsynchronousExecutorImpl getInstance() {
      return INSTANCE;
   }

   @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
   @Override
   public void execute(Runnable command) {
      this.executor
         .submit(
            new AsynchronousExecutorImpl.WrappingRunnable(
               command,
               (String)ELoggerFactory.getLogContext().get("REQUEST_ID"),
               (String)ELoggerFactory.getLogContext().get("GSID"),
               (String)ELoggerFactory.getLogContext().get("CLIENT_IP"),
               (String)ELoggerFactory.getLogContext().get("CONFIG_CONTEXT")
            )
         );
   }

   public boolean stopWaitAndRestart() {
      this.executor.shutdownNow();

      boolean allTasksTerminated;
      try {
         allTasksTerminated = this.executor.awaitTermination(10L, TimeUnit.SECONDS);
      } catch (InterruptedException e) {
         LOG.warn(EDefaultLogType.GENERIC_LOG_TYPE, e);
         return false;
      } finally {
         this.executor = createExecutorService();
      }

      return allTasksTerminated;
   }

   @SuppressFBWarnings(justification = "generated code")
   private AsynchronousExecutorImpl() {
   }

   private static final class WrappingRunnable implements Runnable {
      private final Runnable delegate;
      private final String requestId;
      private final String sessionId;
      private final String clientIp;
      private final String configurationContext;

      WrappingRunnable(Runnable delegate, String requestId, String sessionId, String clientIp, String configurationContext) {
         this.delegate = delegate;
         this.requestId = requestId;
         this.sessionId = sessionId;
         this.clientIp = clientIp;
         this.configurationContext = configurationContext;
      }

      @Override
      public void run() {
         try {
            ELoggerFactory.getLogContext().clear();
            ELoggerFactory.getLogContext().put("REQUEST_ID", this.requestId);
            ELoggerFactory.getLogContext().put("GSID", this.sessionId);
            ELoggerFactory.getLogContext().put("CLIENT_IP", this.clientIp);
            ELoggerFactory.getLogContext().put("CONFIG_CONTEXT", this.configurationContext);
            this.delegate.run();
         } catch (RuntimeException e) {
            AsynchronousExecutorImpl.LOG
               .warn(EDefaultLogType.GENERIC_LOG_TYPE, "Execution of asynchronous event handler failed with exception: " + e.getMessage());
            throw e;
         } finally {
            ELoggerFactory.getLogContext().clear();
         }
      }
   }
}
