package com.airlock.iam.login.app.infrastructure.web;

import com.airlock.iam.base.domain.service.uri.IamUriBuilder;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.Strings;

public class SpaCustomStyles {
   static final String SPA_CUSTOM_STYLES_RELOAD_CONTEXT_ATTRIBUTE = "SPA_CUSTOM_STYLES_RELOAD";
   private static final ELogger LOG = ELoggerFactory.getLogger(SpaCustomStyles.class);
   private static final String DEFAULT_PATH_TO_CUSTOM_STYLES_PROPERTIES = concatPath(Strings.CS.removeStart("/ui", "/"), "styles.properties");
   private final Map<String, String> styleIdentifierToCustomStyleLocation = new ConcurrentHashMap<>();

   public void replaceStyles(String fileHash, HttpServletRequest request) {
      String customStyleLocation = this.getCustomStyleLocation(request, fileHash);
      request.setAttribute("customStyles", customStyleLocation);
   }

   private String getCustomStyleLocation(HttpServletRequest request, String fileHash) {
      this.reloadCustomStyles(request);
      Optional<String> uiResourceSetName = getUiResourceSetName(request);
      String styleIdentifier = uiResourceSetName.orElse(fileHash);
      return this.styleIdentifierToCustomStyleLocation.computeIfAbsent(styleIdentifier, k -> retrieveCustomStyleLocation(fileHash, uiResourceSetName, request));
   }

   private void reloadCustomStyles(HttpServletRequest request) {
      ServletContext servletContext = request.getServletContext();
      Optional<Object> spaCustomStylesReloadFlag = Optional.ofNullable(servletContext.getAttribute("SPA_CUSTOM_STYLES_RELOAD"));
      servletContext.removeAttribute("SPA_CUSTOM_STYLES_RELOAD");
      spaCustomStylesReloadFlag.ifPresent(flag -> this.styleIdentifierToCustomStyleLocation.clear());
   }

   private static Optional<String> getUiResourceSetName(HttpServletRequest request) {
      return Optional.ofNullable(request.getAttribute("uiResourceSetName")).filter(String.class::isInstance).map(String.class::cast);
   }

   private static String retrieveCustomStyleLocation(String fileHash, Optional<String> uiResourceSetName, HttpServletRequest request) {
      return uiResourceSetName.<String>flatMap(resourceSetName -> loadCustomStyleLocationForResourceSet(resourceSetName, request)).or(() -> {
         if (uiResourceSetName.isPresent()) {
            LOG.debug(String.format("Reverting to fallback style at '%s' ", DEFAULT_PATH_TO_CUSTOM_STYLES_PROPERTIES));
         }

         return loadCustomStyleLocation(request, DEFAULT_PATH_TO_CUSTOM_STYLES_PROPERTIES);
      }).orElseGet(() -> {
         String defaultStyleLocation = "styles-" + fileHash + ".css";
         LOG.debug(String.format("Reverting to fallback style at '%s' ", defaultStyleLocation));
         return defaultStyleLocation;
      });
   }

   private static Optional<String> loadCustomStyleLocationForResourceSet(String resourceSetName, HttpServletRequest request) {
      String pathToUiResourceSetCustomStylesProperties = IamUriBuilder.fromPath(Strings.CS.removeStart("/ui", "/"))
         .path(resourceSetName)
         .path("styles.properties")
         .build()
         .getPath();
      return loadCustomStyleLocation(request, pathToUiResourceSetCustomStylesProperties).map(path -> concatPath(resourceSetName, path));
   }

   private static Optional<String> loadCustomStyleLocation(HttpServletRequest request, String pathToStylesProperties) {
      try (InputStream stylePropertiesFile = request.getServletContext().getResourceAsStream(pathToStylesProperties)) {
         if (stylePropertiesFile == null) {
            LOG.debug(String.format("styles.properties at '%s' does not exist.", pathToStylesProperties));
            return Optional.empty();
         } else {
            Properties styleProperties = loadProperties(stylePropertiesFile);
            if (!styleProperties.containsKey("styles")) {
               LOG.debug(String.format("styles.properties at '%s' is missing 'styles' property.", pathToStylesProperties));
               return Optional.empty();
            } else {
               return Optional.of(styleProperties.getProperty("styles"));
            }
         }
      } catch (IOException e) {
         LOG.debug(String.format("styles.properties at '%s' does not exist.", pathToStylesProperties));
         return Optional.empty();
      }
   }

   private static Properties loadProperties(InputStream resource) {
      Properties properties = new Properties();

      try {
         properties.load(resource);
      } catch (IOException e) {
         LOG.debug("Something went wrong closing the stream.");
      }

      return properties;
   }

   private static String concatPath(String uiResourceSetName, String path) {
      return IamUriBuilder.fromPath(uiResourceSetName).path(path).build().getPath();
   }
}
