package com.airlock.iam.application.webserver.infrastructure.deploy;

import com.airlock.iam.application.parameters.domain.model.ApplicationParameters;
import com.airlock.iam.application.parameters.domain.model.HealthCheckParameters;
import com.airlock.iam.application.webserver.infrastructure.filter.NoAccessLogFilter;
import com.airlock.iam.application.webserver.infrastructure.health.LivenessServlet;
import com.airlock.iam.application.webserver.infrastructure.health.ReadinessServlet;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardService;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.Tomcat.FixContextListener;
import org.apache.catalina.valves.RemoteAddrValve;
import org.apache.catalina.valves.RequestFilterValve;

public final class HealthCheckDeployer implements Deployer {
   private final ApplicationParameters parameters;

   public HealthCheckDeployer(ApplicationParameters parameters) {
      this.parameters = parameters;
   }

   @Override
   public Context deploy(Tomcat tomcat) {
      Context context = this.addHealthContext(tomcat);
      context.setSessionTimeout(-1);
      ((StandardContext)context).addValve(allowedIpAddresses(this.parameters.healthCheck()));
      this.addLivenessServlet(context);
      this.addReadinesservlet(context);
      ContextConfigurator.addFilter(context, NoAccessLogFilter.class, "/*");
      return context;
   }

   private Context addHealthContext(Tomcat tomcat) {
      if (this.parameters.healthCheck().port().isPresent()) {
         StandardService healthService = createHealthService(tomcat);
         this.createConnector(healthService, (Integer)this.parameters.healthCheck().port().get());
         StandardEngine healthEngine = createHealthEngine(healthService);
         StandardHost healthHost = createHealthHost(healthEngine);
         return createHealthContext(healthHost);
      } else {
         return tomcat.addContext("/health", null);
      }
   }

   private void createConnector(StandardService healthService, Integer port) {
      Connector healthConnector = new Connector();
      healthConnector.setProperty("address", "0.0.0.0");
      healthConnector.setPort(port);
      healthService.addConnector(healthConnector);
   }

   private static StandardService createHealthService(Tomcat tomcat) {
      StandardService healthService = new StandardService();
      healthService.setName("Health Service");
      tomcat.getServer().addService(healthService);
      return healthService;
   }

   private static StandardEngine createHealthEngine(StandardService healthService) {
      StandardEngine healthEngine = new StandardEngine();
      healthEngine.setName("Health Engine");
      healthEngine.setDefaultHost("localhost");
      healthService.setContainer(healthEngine);
      return healthEngine;
   }

   private static StandardHost createHealthHost(StandardEngine healthEngine) {
      StandardHost healthHost = new StandardHost();
      healthHost.setName("localhost");
      healthEngine.addChild(healthHost);
      return healthHost;
   }

   private static StandardContext createHealthContext(StandardHost healthHost) {
      StandardContext healthContext = new StandardContext();
      healthContext.setName("Health Context");
      healthContext.setPath("/health");
      healthContext.addLifecycleListener(new FixContextListener());
      healthHost.addChild(healthContext);
      return healthContext;
   }

   private void addLivenessServlet(Context context) {
      LivenessServlet servlet = new LivenessServlet(this.parameters);
      Wrapper wrapper = Tomcat.addServlet(context, LivenessServlet.class.getSimpleName(), servlet);
      wrapper.addMapping("/*");
      wrapper.addMapping("/live");
   }

   private void addReadinesservlet(Context context) {
      ReadinessServlet servlet = new ReadinessServlet(this.parameters);
      Wrapper wrapper = Tomcat.addServlet(context, ReadinessServlet.class.getSimpleName(), servlet);
      wrapper.addMapping("/ready");
   }

   private static RequestFilterValve allowedIpAddresses(HealthCheckParameters parameters) {
      RemoteAddrValve valve = new RemoteAddrValve();
      valve.setAllow(parameters.allowedRemoteIpAddresses());
      return valve;
   }
}
