package com.airlock.iam.flow.shared.domain.model.message;

import com.airlock.iam.base.misc.message.GrowableMessageFormat;
import com.airlock.iam.base.misc.message.MessageTemplate;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressFBWarnings(value = "REDOS", justification = "Patterns do not contain nested repetitions")
public final class EncodingMessageTemplate {
   private static final Pattern MESSAGE_FORMAT_VARIABLE = Pattern.compile("\\{[0-9]+(,[^}]+)?}");
   private static final String MARKER_LEFT = "#\u001c\u001d\u001e\u001f";
   private static final String MARKER_RIGHT = "\u001f\u001e\u001d\u001c#";
   private static final Pattern MARKERS_PATTERN = Pattern.compile(
      Pattern.quote("#\u001c\u001d\u001e\u001f") + "(.*?)" + Pattern.quote("\u001f\u001e\u001d\u001c#")
   );
   private final String templateWithMarkers;
   private final Object[] values;
   private final int extraChars;
   private final Function<String, String> encoder;

   public static EncodingMessageTemplate encodingMessageTemplate(MessageTemplate original, Function<String, String> encoder) {
      String withMarkers = addMarkers(original.getTemplate());
      int extraChars = withMarkers.length() - original.getTemplate().length();
      return new EncodingMessageTemplate(withMarkers, original.getValues(), extraChars, encoder);
   }

   public Optional<String> formatGrowableMessage(int maxSize) {
      GrowableMessageFormat messageFormat = new GrowableMessageFormat(this.templateWithMarkers, maxSize + this.extraChars);
      return messageFormat.formatMaximalString(this.values).map(this::removeMarkersAndEncode);
   }

   public String formatMessage() {
      GrowableMessageFormat messageFormat = new GrowableMessageFormat(this.templateWithMarkers, Integer.MAX_VALUE);
      return messageFormat.formatMaximalString(this.values).map(this::removeMarkersAndEncode).orElseThrow();
   }

   private String removeMarkersAndEncode(String message) {
      Matcher matcher = MARKERS_PATTERN.matcher(message);
      StringBuilder builder = new StringBuilder();

      while (matcher.find()) {
         String variable = matcher.group(1);
         matcher.appendReplacement(builder, Matcher.quoteReplacement(this.encoder.apply(variable)));
      }

      matcher.appendTail(builder);
      return builder.toString();
   }

   private static String addMarkers(String template) {
      return MESSAGE_FORMAT_VARIABLE.matcher(template).replaceAll("#\u001c\u001d\u001e\u001f$0\u001f\u001e\u001d\u001c#");
   }

   @SuppressFBWarnings(justification = "generated code")
   private EncodingMessageTemplate(String templateWithMarkers, Object[] values, int extraChars, Function<String, String> encoder) {
      this.templateWithMarkers = templateWithMarkers;
      this.values = values;
      this.extraChars = extraChars;
      this.encoder = encoder;
   }
}
