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

import com.airlock.iam.base.misc.json.JsonCoding;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class I18nCache {
   static final String SPA_I18N_CACHE_RELOAD = "SPA_I18N_CACHE_RELOAD";
   private final I18nJsonLoader productJsonLoader;
   private final I18nJsonLoader customJsonLoader;
   private final I18nCache.CacheType cacheType;
   private final Map<UiResourceIdentifier, I18nCache.ResourceWithCustomJson> resources = new ConcurrentHashMap<>();

   public I18nResource getResource(I18nRequest request) {
      this.clearCacheIfNecessary(request.getRequest());
      UiResourceIdentifier uiResourceIdentifier = request.getUiResourceIdentifier();
      I18nCache.ResourceWithCustomJson resource = this.resources.get(uiResourceIdentifier);
      if (resource == null) {
         resource = this.loadResource(request);
      } else {
         if (this.cacheType == I18nCache.CacheType.IMMUTABLE) {
            return resource.getI18nResource();
         }

         Map<String, Object> customJson = this.loadCustomResource(request.getCustomResourcePath());
         Map<String, Object> currentCustomJson = resource.getCustomJson();
         if (!currentCustomJson.equals(customJson)) {
            resource = this.mergeJson(request, customJson);
         }
      }

      return resource.getI18nResource();
   }

   private void clearCacheIfNecessary(HttpServletRequest req) {
      ServletContext servletContext = req.getServletContext();
      Object attribute = servletContext.getAttribute("SPA_I18N_CACHE_RELOAD");
      servletContext.removeAttribute("SPA_I18N_CACHE_RELOAD");
      if (attribute != null) {
         this.resources.clear();
      }
   }

   private I18nCache.ResourceWithCustomJson loadResource(I18nRequest request) {
      Map<String, Object> customJson = this.loadCustomResource(request.getCustomResourcePath());
      return this.mergeJson(request, customJson);
   }

   private I18nCache.ResourceWithCustomJson mergeJson(I18nRequest request, Map<String, Object> customJson) {
      Map<String, Object> staticJson = this.loadStaticResource(request.getDefaultResourcePath());
      Map<String, Object> mergedJson = JsonCoding.deepMerge(staticJson, customJson);
      String mergedJsonString = JsonCoding.toJson(mergedJson);
      int hashCode = mergedJsonString.hashCode();
      I18nResource resource = I18nResource.builder()
         .content(mergedJsonString)
         .contentHash(Integer.toString(hashCode))
         .lastModified(System.currentTimeMillis())
         .build();
      I18nCache.ResourceWithCustomJson resourceWithBundle = I18nCache.ResourceWithCustomJson.builder().i18nResource(resource).customJson(customJson).build();
      this.resources.put(request.getUiResourceIdentifier(), resourceWithBundle);
      return resourceWithBundle;
   }

   private Map<String, Object> loadCustomResource(String path) {
      return this.customJsonLoader.loadJson(path);
   }

   private Map<String, Object> loadStaticResource(String path) {
      return this.productJsonLoader.loadJson(path);
   }

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

   @SuppressFBWarnings(justification = "generated code")
   public I18nCache(I18nJsonLoader productJsonLoader, I18nJsonLoader customJsonLoader, I18nCache.CacheType cacheType) {
      this.productJsonLoader = productJsonLoader;
      this.customJsonLoader = customJsonLoader;
      this.cacheType = cacheType;
   }

   public enum CacheType {
      MUTABLE,
      IMMUTABLE;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class I18nCacheBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private I18nJsonLoader productJsonLoader;
      @SuppressFBWarnings(justification = "generated code")
      private I18nJsonLoader customJsonLoader;
      @SuppressFBWarnings(justification = "generated code")
      private I18nCache.CacheType cacheType;

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

      @SuppressFBWarnings(justification = "generated code")
      public I18nCache.I18nCacheBuilder productJsonLoader(I18nJsonLoader productJsonLoader) {
         this.productJsonLoader = productJsonLoader;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public I18nCache.I18nCacheBuilder customJsonLoader(I18nJsonLoader customJsonLoader) {
         this.customJsonLoader = customJsonLoader;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public I18nCache.I18nCacheBuilder cacheType(I18nCache.CacheType cacheType) {
         this.cacheType = cacheType;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public I18nCache build() {
         return new I18nCache(this.productJsonLoader, this.customJsonLoader, this.cacheType);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "I18nCache.I18nCacheBuilder(productJsonLoader="
            + this.productJsonLoader
            + ", customJsonLoader="
            + this.customJsonLoader
            + ", cacheType="
            + this.cacheType
            + ")";
      }
   }

   private static final class ResourceWithCustomJson {
      private I18nResource i18nResource;
      private Map<String, Object> customJson;

      @SuppressFBWarnings(justification = "generated code")
      public static I18nCache.ResourceWithCustomJson.ResourceWithCustomJsonBuilder builder() {
         return new I18nCache.ResourceWithCustomJson.ResourceWithCustomJsonBuilder();
      }

      @SuppressFBWarnings(justification = "generated code")
      public I18nResource getI18nResource() {
         return this.i18nResource;
      }

      @SuppressFBWarnings(justification = "generated code")
      public Map<String, Object> getCustomJson() {
         return this.customJson;
      }

      @SuppressFBWarnings(justification = "generated code")
      public ResourceWithCustomJson(I18nResource i18nResource, Map<String, Object> customJson) {
         this.i18nResource = i18nResource;
         this.customJson = customJson;
      }

      @SuppressFBWarnings(justification = "generated code")
      public static class ResourceWithCustomJsonBuilder {
         @SuppressFBWarnings(justification = "generated code")
         private I18nResource i18nResource;
         @SuppressFBWarnings(justification = "generated code")
         private Map<String, Object> customJson;

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

         @SuppressFBWarnings(justification = "generated code")
         public I18nCache.ResourceWithCustomJson.ResourceWithCustomJsonBuilder i18nResource(I18nResource i18nResource) {
            this.i18nResource = i18nResource;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public I18nCache.ResourceWithCustomJson.ResourceWithCustomJsonBuilder customJson(Map<String, Object> customJson) {
            this.customJson = customJson;
            return this;
         }

         @SuppressFBWarnings(justification = "generated code")
         public I18nCache.ResourceWithCustomJson build() {
            return new I18nCache.ResourceWithCustomJson(this.i18nResource, this.customJson);
         }

         @SuppressFBWarnings(justification = "generated code")
         @Override
         public String toString() {
            return "I18nCache.ResourceWithCustomJson.ResourceWithCustomJsonBuilder(i18nResource=" + this.i18nResource + ", customJson=" + this.customJson + ")";
         }
      }
   }
}
