package com.airlock.iam.servicecontainer.api.domain.model.task;

import com.airlock.iam.elog.api.ELogType;
import com.airlock.iam.elog.api.LogTypes;
import com.airlock.iam.log.audit.AuditLogger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public abstract class AbstractTask implements Task {
   public static final int MAXIMUM_OF_LAST_EXECUTION_RESULTS = 5;
   private final AtomicBoolean running = new AtomicBoolean(false);
   private final AtomicBoolean shouldStop = new AtomicBoolean(false);
   private int progress;
   private final List<Task.ExecutionResult> lastExecutions = new ArrayList<>();

   @Override
   public final void executeTask() {
      Stopwatch stopWatch = Stopwatch.createStarted();
      this.progress = 0;
      this.shouldStop.set(false);
      this.running.set(true);

      try {
         AuditLogger.log(this.getClass(), this.getLogType(), this.getThreadNameForLogging() + " starts.");
         this.executeInternal();
         if (this.getProgressPercentage() == 100) {
            this.addRunResult(true);
         } else if (this.getProgressPercentage() > 0 && this.getProgressPercentage() < 100) {
            this.addRunResult(false);
         } else {
            this.addRunResult(true);
         }
      } catch (RuntimeException e) {
         this.addRunResult(false);
         throw e;
      } finally {
         this.shouldStop.set(false);
         this.running.set(false);
         stopWatch.stop();
         AuditLogger.log(
            this.getClass(), this.getLogType(), this.getThreadNameForLogging() + " ends. Running time: " + stopWatch.elapsed(TimeUnit.MILLISECONDS) + "ms."
         );
      }
   }

   protected abstract void executeInternal();

   @Override
   public final void pleaseStop() {
      this.shouldStop.set(true);
   }

   @VisibleForTesting
   public boolean checkShouldStop() {
      return this.shouldStop.get();
   }

   protected final AtomicBoolean getShouldStopBooleanObject() {
      return this.shouldStop;
   }

   @Override
   public final boolean isRunning() {
      return this.running.get();
   }

   @Override
   public final int getProgressPercentage() {
      return this.progress;
   }

   public final void setProgressPercentage(int progressPercentage) {
      this.progress = Math.max(Math.min(progressPercentage, 100), 0);
   }

   @Override
   public String getThreadName() {
      return this.getClass().getSimpleName();
   }

   protected String getThreadNameForLogging() {
      return this.getClass().getSimpleName();
   }

   public ELogType getLogType() {
      return LogTypes.SERVER_INFO_SYS;
   }

   private void addRunResult(boolean result) {
      if (this.lastExecutions.size() >= 5) {
         this.lastExecutions.remove(0);
      }

      this.lastExecutions.add(new Task.ExecutionResult(new Date(), result));
   }

   @Override
   public final List<Task.ExecutionResult> getLastExecutions() {
      return List.copyOf(this.lastExecutions);
   }

   protected void assureTaskFailsIncomplete() {
      int percentage = this.getProgressPercentage();
      if (percentage == 0 || percentage == 100) {
         this.setProgressPercentage(99);
      }
   }
}
