package com.airlock.iam.login.app.misc;

import com.airlock.iam.base.api.domain.model.exception.ServiceConfigurationException;
import com.airlock.iam.common.domain.model.session.SessionId;
import com.airlock.iam.common.domain.service.session.CorrelationIdService;
import com.airlock.iam.common.infrastructure.request.HttpServletRequestInformationServiceImpl;
import com.airlock.iam.common.infrastructure.session.CachingCorrelationIdService;
import com.airlock.iam.common.infrastructure.session.CorrelationIdServiceImpl;
import com.airlock.iam.core.application.configuration.waf.AirlockGatewayConfig;
import com.airlock.iam.core.misc.util.airlock.AirlockWAFEnvValues;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.login.app.infrastructure.reporting.RequestEnvironmentReportingDataProvider;
import com.airlock.iam.login.app.misc.configuration.Loginapp;
import com.airlock.iam.login.app.misc.configuration.oneshot.OneShotAuthenticationConfig;
import com.airlock.iam.login.app.misc.configuration.oneshot.OneShotTargetApplication;
import com.airlock.iam.login.app.misc.oneshot.AirlockOneShotAuthenticationWorkflow;
import com.airlock.iam.login.app.misc.oneshot.OneShotContext;
import com.airlock.iam.login.app.misc.session.UserSpecificRoleTimeoutCalculator;
import com.airlock.iam.login.app.misc.util.ServletConfigUtils;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.reporting.api.domain.model.data.Channel;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import java.util.Optional;

public class OneShotLoginServlet extends ExceptionWrapperServlet {
   private static final ELogger LOG = ELoggerFactory.getLogger(OneShotLoginServlet.class);

   @Override
   protected void handleRequest(HttpServletRequest request, HttpServletResponse response, ConfigurationContext context) throws Exception {
      RequestEnvironmentReportingDataProvider.requestEnvironmentReportingData().channel(Channel.ONE_SHOT);

      try {
         Optional<OneShotAuthenticationConfig> config = Loginapp.get(context).getOneShotSettings();
         if (!config.isEmpty()) {
            OneShotContext oneShotContext = new OneShotContext(
               context,
               ServletConfigUtils.determineSslSessionId(request, context),
               ServletConfigUtils.determineClientIpAddress(request, context).getIpAddress(),
               ServletConfigUtils.determineDisplayLanguage(request, context),
               determineSessionId(request)
            );
            AirlockOneShotAuthenticationWorkflow workflow = createWorkflow(request, context, config.get());
            workflow.authenticate(request, response, oneShotContext);
            return;
         }

         LOG.warn(LogTypes.AIRLOCK_ONESHOT, "Airlock One-Shot not configured, but still receiving requests");
         response.sendError(404, "Not found");
      } catch (RuntimeException e) {
         LOG.warn(LogTypes.AIRLOCK_ONESHOT, "Exception during one-shot request", e);
         return;
      } finally {
         HttpSession httpSession = request.getSession(false);
         if (httpSession != null && httpSession.isNew()) {
            httpSession.invalidate();
         }
      }
   }

   private static SessionId determineSessionId(HttpServletRequest request) {
      CorrelationIdService correlationIdService = new CachingCorrelationIdService(
         () -> new CorrelationIdServiceImpl(() -> request, () -> new HttpServletRequestInformationServiceImpl(() -> request), Optional::empty),
         request::getSession
      );
      return correlationIdService.determineNewSessionId();
   }

   @Override
   protected void logRequestStart(HttpServletRequest request) {
   }

   private static AirlockOneShotAuthenticationWorkflow createWorkflow(
      HttpServletRequest request, ConfigurationContext context, OneShotAuthenticationConfig config
   ) {
      AirlockGatewayConfig wafConfig = ServiceConfigurationException.checkPresent(
         ServletConfigUtils.findGatewayConfig(context), "Airlock One-Shot needs a Airlock WAF configuration"
      );
      AirlockWAFEnvValues wafEnv = new AirlockWAFEnvValues(request.getCookies(), wafConfig);
      LOG.debug("Starting Airlock One-Shot Authentication for original URL " + OneShotAuthenticationConfig.extractAccessedURL(wafEnv).orElse("<none>"));
      OneShotTargetApplication targetApp = config.selectTargetApplication(wafEnv);
      return targetApp.createWorkflow(wafConfig, context, new UserSpecificRoleTimeoutCalculator(config.getUserSpecificRoleTimeouts()));
   }
}
