package com.airlock.iam.base.misc.message;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MessageTemplate {
   private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\$\\{([a-zA-Z][a-zA-Z0-9._-]*)([^}]*)}");
   private static final String OPENING_MARKER = "#\u001c\u001d\u001e\u001f";
   private static final Pattern OPENING_MARKER_PATTERN = Pattern.compile(Pattern.quote("#\u001c\u001d\u001e\u001f"));
   private static final String CLOSING_MARKER = "\u001f\u001e\u001d\u001c#";
   private static final Pattern CLOSING_MARKER_PATTERN = Pattern.compile(Pattern.quote("\u001f\u001e\u001d\u001c#"));
   private final String template;
   private final Object[] values;

   public static MessageTemplate fromIndexedParameters(String template, Object[] values) {
      return new MessageTemplate.ParametrizedTemplate(template, values).escapeSingleQuotes().toMessageTemplate();
   }

   public static MessageTemplate fromNamedParameters(String template, Map<String, Object> values) {
      return parametrizeTemplate(template, values).escapeSingleQuotes().escapeCurlyBraces().replaceMarkers().toMessageTemplate();
   }

   public Optional<String> formatGrowableMessage(int maxSize) {
      GrowableMessageFormat messageFormat = new GrowableMessageFormat(this.template, maxSize);
      return messageFormat.formatMaximalString(this.values);
   }

   public Optional<String> formatNonGrowableMessage() {
      return this.formatGrowableMessage(Integer.MAX_VALUE);
   }

   private static MessageTemplate.ParametrizedTemplate parametrizeTemplate(String template, Map<String, Object> values) {
      Matcher matcher = VARIABLE_PATTERN.matcher(template);
      StringBuilder builder = new StringBuilder();
      ArrayList<Object> valueArray = new ArrayList<>();
      int index = 0;

      while (matcher.find()) {
         String variable = matcher.group(1);
         Object value = values.get(variable);
         if (value != null) {
            valueArray.add(value);
            matcher.appendReplacement(builder, "#\u001c\u001d\u001e\u001f" + index++ + matcher.group(2) + "\u001f\u001e\u001d\u001c#");
         } else {
            matcher.appendReplacement(builder, "");
         }
      }

      matcher.appendTail(builder);
      return new MessageTemplate.ParametrizedTemplate(builder.toString(), valueArray.toArray());
   }

   @SuppressFBWarnings(justification = "generated code")
   private MessageTemplate(String template, Object[] values) {
      this.template = template;
      this.values = values;
   }

   @SuppressFBWarnings(justification = "generated code")
   public String getTemplate() {
      return this.template;
   }

   @SuppressFBWarnings(justification = "generated code")
   public Object[] getValues() {
      return this.values;
   }

   @SuppressFBWarnings(justification = "generated code")
   @Override
   public String toString() {
      return "MessageTemplate(template=" + this.getTemplate() + ", values=" + Arrays.deepToString(this.getValues()) + ")";
   }

   private static final class ParametrizedTemplate {
      private String template;
      private final Object[] values;

      private MessageTemplate.ParametrizedTemplate escapeSingleQuotes() {
         this.template = MessageFormatSingleQuoteEscaper.escapeSingleQuotes(this.template);
         return this;
      }

      private MessageTemplate.ParametrizedTemplate escapeCurlyBraces() {
         this.template = MessageFormatCurlyBraceEscaper.escapeCurlyBraces(this.template);
         return this;
      }

      private MessageTemplate.ParametrizedTemplate replaceMarkers() {
         this.template = MessageTemplate.OPENING_MARKER_PATTERN.matcher(this.template).replaceAll("{");
         this.template = MessageTemplate.CLOSING_MARKER_PATTERN.matcher(this.template).replaceAll("}");
         return this;
      }

      private MessageTemplate toMessageTemplate() {
         return new MessageTemplate(this.template, this.values);
      }

      @SuppressFBWarnings(justification = "generated code")
      public ParametrizedTemplate(String template, Object[] values) {
         this.template = template;
         this.values = values;
      }
   }
}
