
package de.ivu.elect.jee.presentation.dynamicform.converter;

import static java.util.Arrays.asList;
import static org.apache.commons.lang3.StringUtils.isAlphanumeric;
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
import static org.apache.commons.lang3.StringUtils.isNumeric;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;

public class AutoFormatDateFormatter implements Serializable {

  private static final long serialVersionUID = 1L;

  private final String pattern;

  private final List<String> patternParts;

  private final boolean pattern2DigitFutureYearConvertsToPastCentury;

  public AutoFormatDateFormatter(String pattern, boolean pattern2DigitFutureYearConvertsToPastCentury) {
    super();

    this.pattern = trimToEmpty(pattern);
    this.patternParts = asList(this.pattern.split("[^a-zA-Z]"));
    this.pattern2DigitFutureYearConvertsToPastCentury = pattern2DigitFutureYearConvertsToPastCentury;
  }

  public String getPattern() {
    return pattern;
  }

  public String tryAutoFormatValue(String value) {
    if (isNumeric(value)) {
      return tryAutoFormatValueNumeric(value).orElse(value);
    }

    return tryAutoFormatValueFormatted(value).orElse(value);
  }

  private Optional<String> tryAutoFormatValueNumeric(String valueNumeric) {
    boolean valueContains4DigitYear = isValueContaining4DigitYear(valueNumeric);
    boolean valueContains2DigitYear = isValueContaining2DigitYear(valueNumeric);

    if (valueContains4DigitYear || valueContains2DigitYear) {
      String value = trimToEmpty(valueNumeric);
      String valueFormattedPattern = trimToEmpty(pattern);

      Map<Integer, String> valueFormattedPatternSeparatorPerIndexSortedAsc = collectPatternSeparatorPerIndexSortedAsc(valueFormattedPattern);

      if (valueContains2DigitYear) {
        valueFormattedPattern = valueFormattedPattern.replace("yyyy", "yy");
        valueFormattedPatternSeparatorPerIndexSortedAsc = collectPatternSeparatorPerIndexSortedAsc(valueFormattedPattern);
      }

      StringBuilder valueFormatted = new StringBuilder(value);

      valueFormattedPatternSeparatorPerIndexSortedAsc.forEach((index, inputPatternSeparator) -> {
        if (index < valueFormatted.length()) {
          valueFormatted.insert(index, inputPatternSeparator);
        } else {
          valueFormatted.append(inputPatternSeparator);
        }
      });

      return tryAutoFormatValueFormatted(valueFormatted.toString());
    }

    return Optional.empty();
  }

  private boolean isValueContaining4DigitYear(String value) {
    return calculateValueLength(value) == calculateValueLength4DigitYear(value);
  }

  private boolean isValueContaining2DigitYear(String value) {
    return calculateValueLength(value) == calculateValueLength4DigitYear(value) - 2;
  }

  private int calculateValueLength(String value) {
    return trimToEmpty(value).length();
  }

  private int calculateValueLength4DigitYear(String value) {
    if (isNoneBlank(value, pattern)) {
      return pattern.length() - collectPatternSeparatorPerIndexSortedAsc(pattern).size();
    }

    return -1;
  }

  private Map<Integer, String> collectPatternSeparatorPerIndexSortedAsc(String pattern) {
    Map<Integer, String> resultSortedAsc = new TreeMap<>();

    for (int index = 0; index < pattern.length(); index++) {
      String character = Character.toString(pattern.charAt(index));

      if (!isAlphanumeric(character)) {
        resultSortedAsc.put(index, character);
      }
    }

    return resultSortedAsc;
  }

  private Optional<String> tryAutoFormatValueFormatted(String valueFormatted) {
    String value = trimToEmpty(valueFormatted);
    List<String> valueParts = asList(value.split("\\D"));

    if (patternParts.size() == valueParts.size()) {
      String result = pattern;

      for (int i = 0; i < patternParts.size(); i++) {
        String patternPart = patternParts.get(i);

        if (isAlphanumeric(patternPart)) {
          String valuePart = valueParts.get(i);

          if (!isNumeric(valuePart)) {
            result = "";

            // Keine Zahl, das war's...
            break;
          }

          if ("yyyy".equals(patternPart) && valuePart.length() == 2) {
            try {
              // NOTE, dass das Problem an dieser Stelle ist, dass bei Verwendung von 'yyyy' und bei einer Eingabe als
              // zweistellige Jahreszahl '81' ein GÜLTIGES Datum (ohne Exception) mit dem Jahr '0081' geparsed wird.
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(new SimpleDateFormat("dd.MM.yy").parse("01.01." + valuePart));

              if (pattern2DigitFutureYearConvertsToPastCentury) { // ELECTMUC-2429
                calendar = convertFutureYearToPastCentury(calendar);
              }

              valuePart = new SimpleDateFormat("yyyy").format(calendar.getTime());
            } catch (Exception ignored) {
            }
          }

          result = result.replace(patternPart, leftPad(valuePart, patternPart.length(), "0"));
        }
      }

      if (result.length() == pattern.length()) {
        return Optional.of(result);
      }
    }

    return Optional.empty();
  }

  static Calendar convertFutureYearToPastCentury(Calendar calendar) {
    Calendar today = Calendar.getInstance();
    today.set(Calendar.HOUR_OF_DAY, 23);
    today.set(Calendar.MINUTE, 59);
    today.set(Calendar.SECOND, 59);
    today.set(Calendar.MILLISECOND, 999);

    if (calendar.after(today)) {
      calendar.add(Calendar.YEAR, -100);
    }

    return calendar;
  }
}
