package com.airlock.iam.servicecontainer.app.infrastructure.webapp.view;

import com.airlock.iam.common.domain.model.logs.CorrelationIdSettings;
import com.airlock.iam.common.domain.model.request.ClientIp;
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.common.infrastructure.session.LogContextInitializer;
import com.airlock.iam.common.infrastructure.session.LogContextInitializerImpl;
import com.airlock.iam.core.misc.util.config.Config;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.servicecontainer.app.application.configuration.Server;
import com.airlock.iam.servicecontainer.app.infrastructure.server.Controller;
import com.airlock.iam.servicecontainer.app.infrastructure.webapp.model.ServiceContainerException;
import com.airlock.iam.servicecontainer.app.infrastructure.webapp.model.ServiceContainerModel;
import com.airlock.iam.servicecontainer.app.infrastructure.webapp.util.ErrorMessage;
import com.airlock.iam.servicecontainer.app.infrastructure.webapp.util.ServiceContainerVaadinTranslator;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Widgetset;
import com.vaadin.navigator.Navigator;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServletRequest;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.WrappedSession;
import com.vaadin.ui.UI;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Locale;
import java.util.Optional;
import java.util.TreeMap;

@Theme("medusa")
@Widgetset("com.vaadin.v7.Vaadin7WidgetSet")
public class ServiceContainerUI extends UI {
   private static final int POLL_INTERVAL_IN_MILLIS = 1000;
   private static final String ERROR_VIEW = "error";
   public static final String CONTENT_VIEW = "";
   private final ServiceContainerModel serviceContainerModel = new ServiceContainerModel(new TreeMap<>());
   private Navigator navigator;
   private ContentView contentView;

   protected void init(VaadinRequest request) {
      this.setSessionIdleTimeout(request.getWrappedSession(true));
      this.initLanguage(request);
      Page.getCurrent().setTitle(ServiceContainerVaadinTranslator.translate("application.title"));
      if (this.serviceContainerModel.isUserAlreadyLoggedIn()) {
         if (request.getParameter("sso") != null) {
            this.redirectWithoutSsoTicketParameter(request);
            return;
         }

         this.initLogContext(request);
         this.handleRefresh();
         this.ensureUpdateMechanism();
      } else {
         this.handleFirstRequest(request);
      }
   }

   private void setSessionIdleTimeout(WrappedSession session) {
      session.setMaxInactiveInterval(Math.toIntExact(Config.getSessionIdleTimeout(ConfigurationContext.defaultContext()).toSeconds()));
   }

   private void ensureUpdateMechanism() {
      this.setPollInterval(1000);
      ServiceContainerUiUpdaterThread uiUpdaterThread = Controller.getInstance().getUiUpdaterThread();
      uiUpdaterThread.addUI(this);
   }

   private void initLogContext(VaadinRequest request) {
      if (request instanceof VaadinServletRequest vaadinServletRequest) {
         HttpServletRequest httpServletRequest = vaadinServletRequest.getHttpServletRequest();
         CorrelationIdService correlationIdService = new CachingCorrelationIdService(
            () -> new CorrelationIdServiceImpl(
               () -> httpServletRequest,
               () -> new HttpServletRequestInformationServiceImpl(() -> httpServletRequest),
               ServiceContainerUI::getCorrelationIdSettings
            ),
            httpServletRequest::getSession
         );
         LogContextInitializer logContextInitializer = logContextInitializer(request, httpServletRequest, correlationIdService);
         logContextInitializer.ensureInitialized();
      }
   }

   private static LogContextInitializer logContextInitializer(
      VaadinRequest request, HttpServletRequest httpServletRequest, CorrelationIdService correlationIdService
   ) {
      return new LogContextInitializerImpl(
         () -> httpServletRequest,
         correlationIdService::determineNewSessionId,
         correlationIdService::determineRequestId,
         () -> new ClientIp(request.getRemoteAddr()),
         correlationIdService::determineCorrelationId
      );
   }

   private static Optional<CorrelationIdSettings> getCorrelationIdSettings() {
      return Server.get()
         .getCorrelationIdSettingsConfig()
         .map(config -> CorrelationIdSettings.builder().headerName(config.getHeaderName()).validationPattern(config.getValidationPattern()).build());
   }

   private void handleFirstRequest(VaadinRequest request) {
      try {
         String ssoTicketString = request.getParameter("sso");
         if (ssoTicketString == null) {
            this.navigator = new Navigator(this, this);
            this.navigator
               .addView(
                  "error",
                  new ErrorView(
                     new ErrorMessage(
                        ErrorMessage.ErrorCaption.AUTHENTICATION_FAILED_NO_TICKET_FOUND, ErrorMessage.ErrorDescription.AUTHENTICATION_FAILED_NO_TICKET_FOUND
                     )
                  )
               );
            this.navigator.navigateTo("error");
         } else {
            this.serviceContainerModel.authenticateUserWithSsoTicket(ssoTicketString);
            this.redirectWithoutSsoTicketParameter(request);
         }
      } catch (ServiceContainerException e) {
         this.navigator = new Navigator(this, this);
         this.navigator.addView("error", e.getErrorView());
         this.navigator.navigateTo("error");
      }
   }

   private void redirectWithoutSsoTicketParameter(VaadinRequest request) {
      String requestLanguage = request.getParameter("lang");
      String serviceContainerSsoPathWithoutSsoTicket = String.format("%s?%s=%s", "serviceContainer", "lang", requestLanguage);
      this.getUI().getPage().setLocation(serviceContainerSsoPathWithoutSsoTicket);
   }

   private void handleRefresh() {
      this.navigator = new Navigator(this, this);
      this.contentView = new ContentView(this.navigator, this.serviceContainerModel);
      this.navigator.addView("", this.contentView);
      this.contentView.init();
   }

   private void initLanguage(VaadinRequest request) {
      String requestLanguage = request.getParameter("lang");
      if (requestLanguage != null) {
         if ("de".equals(requestLanguage)) {
            VaadinSession.getCurrent().setLocale(Locale.GERMAN);
         } else if ("fr".equals(requestLanguage)) {
            VaadinSession.getCurrent().setLocale(Locale.FRENCH);
         } else {
            VaadinSession.getCurrent().setLocale(Locale.ENGLISH);
         }
      } else {
         VaadinSession.getCurrent().setLocale(Locale.ENGLISH);
      }
   }

   ContentView getContentView() {
      return this.contentView;
   }
}
