package com.airlock.iam.common.infrastructure.delay;

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 jakarta.ws.rs.container.ContainerResponseContext;
import java.time.Duration;
import java.time.Instant;

public final class ResponseDelayServiceImpl implements ResponseDelayService {
   private static final ELogger LOG = ELoggerFactory.getLogger(ResponseDelayServiceImpl.class);
   private final Instant requestEntry = Instant.now();
   private final ResponseDelaySettings settings;

   @Override
   public void delayIfNecessary(ContainerResponseContext response) {
      if (this.settings.condition().responseDelayRequired(response)) {
         Duration actualDuration = Duration.between(this.requestEntry, Instant.now());
         Duration requiredDelay = this.settings.responseDelay().minus(actualDuration);
         if (requiredDelay.isPositive()) {
            try {
               LOG.debug(
                  String.format(
                     "Delaying request by '%dms' to reach fixed response duration of '%dms'.",
                     requiredDelay.toMillis(),
                     this.settings.responseDelay().toMillis()
                  )
               );
               Thread.sleep(requiredDelay);
            } catch (InterruptedException e) {
               LOG.error(EDefaultLogType.GENERIC_LOG_TYPE, "Thread interrupted while waiting for fixed response duration.", e);
            }
         } else if (!this.settings.responseDelay().isZero()) {
            LOG.warn(
               EDefaultLogType.GENERIC_LOG_TYPE,
               String.format(
                  "Actual request duration of '%dms' exceeded configured '%dms'. This may allow timing attacks.",
                  actualDuration.toMillis(),
                  this.settings.responseDelay().toMillis()
               )
            );
         }
      }
   }

   @SuppressFBWarnings(justification = "generated code")
   public Instant getRequestEntry() {
      return this.requestEntry;
   }

   @SuppressFBWarnings(justification = "generated code")
   public ResponseDelaySettings getSettings() {
      return this.settings;
   }

   @SuppressFBWarnings(justification = "generated code")
   public ResponseDelayServiceImpl(ResponseDelaySettings settings) {
      this.settings = settings;
   }
}
