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

import com.airlock.iam.common.application.service.session.AirlockControlCookieService;
import com.airlock.iam.common.application.session.AlreadyAddedAirlockControlCookies;
import com.airlock.iam.common.domain.model.auth.idpropagation.WafCredential;
import com.airlock.iam.common.domain.model.gateway.WafSettings;
import com.airlock.iam.core.misc.impl.sso.HttpHeaderIdentityPropagator;
import com.airlock.iam.core.misc.util.airlock.AirlockControlCookie;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.log.api.signing.SigningLogger;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.ArrayUtils;

public final class AirlockControlCookieServiceImpl implements AirlockControlCookieService {
   private final Provider<Optional<WafSettings>> wafSettings;
   private final Provider<HttpServletResponse> response;
   private final SigningLogger signingLogger;
   private final Provider<AlreadyAddedAirlockControlCookies> alreadyAddedCookies;
   private static final ELogger LOG = ELoggerFactory.getLogger(AirlockControlCookieServiceImpl.class);

   @Inject
   public AirlockControlCookieServiceImpl(
      Provider<Optional<WafSettings>> wafSettings,
      Provider<HttpServletResponse> response,
      SigningLogger signingLogger,
      Provider<AlreadyAddedAirlockControlCookies> alreadyAddedCookies
   ) {
      this.wafSettings = wafSettings;
      this.response = response;
      this.signingLogger = signingLogger;
      this.alreadyAddedCookies = alreadyAddedCookies;
   }

   @Override
   public void terminateAirlockSession() {
      if (this.wafSettingsOrLog("not terminating WAF session").isPresent()) {
         AirlockControlCookie cookie = new AirlockControlCookie();
         cookie.terminateSession();
         cookie.forceNewSessionId();
         this.addControlCookieToResponse(cookie);
      }
   }

   @Override
   public void clearAirlockSession() {
      if (this.wafSettingsOrLog("not clearing WAF session").isPresent()) {
         AirlockControlCookie cookie = new AirlockControlCookie();
         cookie.clearSession();
         cookie.forceNewSessionId();
         this.addControlCookieToResponse(cookie);
      }
   }

   @Override
   public void addCredentialsWithKeepSessionId(Collection<WafCredential> wafCredentials) {
      if (!wafCredentials.isEmpty()) {
         this.addCredentialsWithKeepSessionId(formatForWaf(wafCredentials));
      }
   }

   @Override
   public void addCredentialsWithKeepSessionId(String[] credentials) {
      if (!ArrayUtils.isEmpty(credentials)) {
         if (this.wafSettingsOrLog("not adding/setting credentials.").isPresent()) {
            AirlockControlCookie cookie = new AirlockControlCookie();
            if (((WafSettings)((Optional)this.wafSettings.get()).get()).isMustAddCredentialsToSession()) {
               cookie.setAddCredentialsWithKeepSessionId(credentials);
            } else {
               cookie.replaceCredentialsWithKeepSessionId(credentials);
            }

            this.addControlCookieToResponse(cookie);
         }
      }
   }

   @Override
   public void setCredentials(Collection<WafCredential> wafCredentials) {
      if (!wafCredentials.isEmpty()) {
         this.setCredentials(formatForWaf(wafCredentials));
      }
   }

   private static String[] formatForWaf(Collection<WafCredential> wafCredentials) {
      return wafCredentials.stream().map(WafCredential::formatForWaf).toArray(String[]::new);
   }

   private void setCredentials(String[] credentials) {
      if (!ArrayUtils.isEmpty(credentials)) {
         if (this.wafSettingsOrLog("not setting credentials.").isPresent()) {
            AirlockControlCookie cookie = new AirlockControlCookie();
            cookie.replaceCredentials(credentials);
            this.addControlCookieToResponse(cookie);
         }
      }
   }

   @Override
   public void addRequestHeader(String name, String value, List<String> mappings) {
      if (this.wafSettingsOrLog("not setting request header.").isPresent()) {
         HttpHeaderIdentityPropagator.AddHeaderControlCookie cookie = new HttpHeaderIdentityPropagator.AddHeaderControlCookie(
            ((WafSettings)((Optional)this.wafSettings.get()).get()).getControlCookieName()
         );
         cookie.addHeaderInfo(name, value, mappings);
         ((HttpServletResponse)this.response.get()).addCookie(cookie.getAirlockControlCookie().get());
      }
   }

   @Override
   public void forceNewSessionId() {
      if (this.wafSettingsOrLog("not forcing new session id.").isPresent()) {
         AirlockControlCookie cookie = new AirlockControlCookie();
         cookie.forceNewSessionId();
         this.addControlCookieToResponse(cookie);
      }
   }

   @Override
   public void terminateGSIDs(List<String> gsids) {
      if (this.wafSettingsOrLog("not terminating any sessions.").isPresent()) {
         if (gsids.isEmpty()) {
            LOG.debug("List of GSIDs is empty, not terminating any sessions.");
            return;
         }

         AirlockControlCookie cookie = new AirlockControlCookie();
         gsids.forEach(cookie::terminateGSID);
         this.addControlCookieToResponse(cookie);
      }
   }

   @Override
   public void setAuditToken(String auditToken) {
      if (this.wafSettingsOrLog("No WAF configured, cannot set audit token").isPresent()) {
         AirlockControlCookie cookie = new AirlockControlCookie();
         cookie.setAuditToken(auditToken);
         this.addControlCookieToResponse(cookie);
      }
   }

   @Override
   public void addOneShotWorkflowAction(AirlockControlCookieService.OneShotWorkflowAction action) {
      AirlockControlCookie cookie = new AirlockControlCookie();
      if (action == AirlockControlCookieService.OneShotWorkflowAction.CONTINUE) {
         cookie.authFlowContinue();
      } else {
         cookie.authFlowFinalResponse();
      }

      this.addControlCookieToResponse(cookie);
   }

   private void addControlCookieToResponse(AirlockControlCookie cookie) {
      if (((AlreadyAddedAirlockControlCookies)this.alreadyAddedCookies.get()).contains(cookie)) {
         LOG.debug("Not Setting Airlock control cookie since it is already present: " + cookie.getCookieValue());
      } else {
         ((HttpServletResponse)this.response.get())
            .addCookie(cookie.getControlCookie(((WafSettings)((Optional)this.wafSettings.get()).get()).getControlCookieName()));
         ((AlreadyAddedAirlockControlCookies)this.alreadyAddedCookies.get()).add(cookie);
         this.logCookieValue(cookie);
      }
   }

   private void logCookieValue(AirlockControlCookie cookie) {
      this.signingLogger.log(String.format("Setting Airlock control cookie (value): %s", cookie.getCookieValue()));
   }

   private Optional<WafSettings> wafSettingsOrLog(String logMessage) {
      if (((Optional)this.wafSettings.get()).isEmpty()) {
         LOG.debug("No WAF configured, " + logMessage);
      }

      return (Optional<WafSettings>)this.wafSettings.get();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder builder() {
      return new AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class AirlockControlCookieServiceImplBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Provider<Optional<WafSettings>> wafSettings;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<HttpServletResponse> response;
      @SuppressFBWarnings(justification = "generated code")
      private SigningLogger signingLogger;
      @SuppressFBWarnings(justification = "generated code")
      private Provider<AlreadyAddedAirlockControlCookies> alreadyAddedCookies;

      @SuppressFBWarnings(justification = "generated code")
      AirlockControlCookieServiceImplBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder wafSettings(Provider<Optional<WafSettings>> wafSettings) {
         this.wafSettings = wafSettings;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder response(Provider<HttpServletResponse> response) {
         this.response = response;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder signingLogger(SigningLogger signingLogger) {
         this.signingLogger = signingLogger;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder alreadyAddedCookies(
         Provider<AlreadyAddedAirlockControlCookies> alreadyAddedCookies
      ) {
         this.alreadyAddedCookies = alreadyAddedCookies;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public AirlockControlCookieServiceImpl build() {
         return new AirlockControlCookieServiceImpl(this.wafSettings, this.response, this.signingLogger, this.alreadyAddedCookies);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "AirlockControlCookieServiceImpl.AirlockControlCookieServiceImplBuilder(wafSettings="
            + this.wafSettings
            + ", response="
            + this.response
            + ", signingLogger="
            + this.signingLogger
            + ", alreadyAddedCookies="
            + this.alreadyAddedCookies
            + ")";
      }
   }
}
