
package de.ivu.elect.business.entschaedigung.entity;

import static java.util.Collections.emptyList;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Supplier;

import de.ivu.elect.business.entschaedigung.boundary.AbstractAuszahlungDAO;
import de.ivu.elect.business.person.entity.PersonendatenArt;
import de.ivu.elect.util.MoneyUtil;

public abstract class AbstractEntschaedigungsart implements EntschaedigungsartBase {

  private static final long serialVersionUID = 1L;

  public static final String MESSAGE_KEY_EINZELBETRAG_EINZAHL = "entity.einzahl.ENTSCHAEDIGUNGSART.EINZELBETRAG";

  public static final String MESSAGE_KEY_EINZELBETRAG_EINZAHL_VARIABEL = MESSAGE_KEY_EINZELBETRAG_EINZAHL + ".VARIABEL";

  public static final String MESSAGE_KEY_EINZELBETRAG_MAXIMAL_EINZAHL_UNBESCHRAENKT = EntschaedigungsartConstants.MESSAGE_KEY_EINZELBETRAG_MAXIMAL_EINZAHL + ".UNBESCHRAENKT";

  public AbstractEntschaedigungsart() {
    super();
  }

  public boolean isBearbeitbar(AbstractAuszahlungDAO auszahlungDAO) {
    return !hasAuszahlung(auszahlungDAO);
  }

  public boolean hasAuszahlung(AbstractAuszahlungDAO auszahlungDAO) {
    return !getAuszahlungen(auszahlungDAO).isEmpty();
  }

  public Collection<Auszahlung> getAuszahlungen(AbstractAuszahlungDAO auszahlungDAO) {
    Entschaedigungsart entschaedigungsart = (Entschaedigungsart) this;

    if (entschaedigungsart.isPersistent()) {
      return auszahlungDAO.findByEntschaedigungsart(entschaedigungsart);
    }

    return emptyList();
  }

  public String getErklaerung() {
    return trimToEmpty(defaultString(getBeschreibung(), getBezeichnung()));
  }

  public boolean isKriteriumEinsatzIrrelevant() {
    return isKriteriumEinsatzfunktionIrrelevant() && isKriteriumEinsatzortIrrelevant();
  }

  public boolean matchesKriteriumEinsatz(EntschaedigungsartKriteriumEinsatzort kriteriumEinsatzort, EntschaedigungsartKriteriumEinsatzfunktion kriteriumEinsatzfunktion) {
    return kriteriumEinsatzort != null && kriteriumEinsatzfunktion != null && matchesKriteriumEinsatz(kriteriumEinsatzort.getTechnicalName(), kriteriumEinsatzfunktion.getTechnicalName());
  }

  protected boolean matchesKriteriumEinsatz(String kriteriumEinsatzort, String kriteriumEinsatzfunktion) {
    return matchesKriteriumEinsatzfunktion(kriteriumEinsatzfunktion) && matchesKriteriumEinsatzort(kriteriumEinsatzort);
  }

  public boolean isBerechnetPerEinsatz() {
    return EntschaedigungsartBerechnungsart.PER_EINSATZ.equals(getBerechnungsart());
  }

  public boolean isBerechnetPerAnzahl() {
    return EntschaedigungsartBerechnungsart.PER_ANZAHL.equals(getBerechnungsart());
  }

  public boolean isEinzelbetragVariabel() {
    return hasEinzelbetragMinimal() && (!hasEinzelbetragMaximal() || getEinzelbetragMaximal().compareTo(getEinzelbetragMinimal()) > 0);
  }

  public boolean hasEinzelbetragMinimal() {
    return getEinzelbetragMinimal() != null;
  }

  public String getEinzelbetragMinimalMitWaehrung(Locale locale) {
    return formatBetragMitWaehrung(locale, getEinzelbetragMinimal());
  }

  protected BigDecimal preProcessEinzelbetragMinimal(BigDecimal neuerWert) {
    return roundBetrag(neuerWert);
  }

  protected void preSetEinzelbetragMinimal(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  protected void postSetEinzelbetragMinimal(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  public boolean hasEinzelbetragMaximal() {
    return getEinzelbetragMaximal() != null;
  }

  public String getEinzelbetragMaximalMitWaehrung(Locale locale) {
    return formatBetragMitWaehrung(locale, getEinzelbetragMaximal());
  }

  protected BigDecimal preProcessEinzelbetragMaximal(BigDecimal neuerWert) {
    return roundBetrag(neuerWert);
  }

  protected void preSetEinzelbetragMaximal(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  protected void postSetEinzelbetragMaximal(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  public boolean hasEinzelbetragInitial() {
    return getEinzelbetragInitial() != null;
  }

  public String getEinzelbetragInitialMitWaehrung(Locale locale) {
    return formatBetragMitWaehrung(locale, getEinzelbetragInitial());
  }

  protected BigDecimal preProcessEinzelbetragInitial(BigDecimal neuerWert) {
    return roundBetrag(neuerWert);
  }

  protected void preSetEinzelbetragInitial(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  protected void postSetEinzelbetragInitial(BigDecimal alterWert, BigDecimal neuerWert) {
    // Do nothing.
  }

  public boolean isAnzahlVariabel() {
    return hasAnzahlMinimal() && (!hasAnzahlMaximal() || getAnzahlMaximal().compareTo(getAnzahlMinimal()) > 0);
  }

  public boolean hasAnzahlMinimal() {
    return getAnzahlMinimal() != null;
  }

  public boolean usesAnzahlMinimal(Long anzahl) {
    return Objects.equals(getAnzahlMinimal(), anzahl);
  }

  public boolean hasAnzahlMaximal() {
    return getAnzahlMaximal() != null;
  }

  public boolean usesAnzahlMaximal(Long anzahl) {
    return Objects.equals(getAnzahlMaximal(), anzahl);
  }

  public boolean hasAnzahlInitial() {
    return getAnzahlInitial() != null;
  }

  public boolean usesAnzahlInitial(Long anzahl) {
    return Objects.equals(getAnzahlInitial(), anzahl);
  }

  /**
   * NOTE, dass die Unterscheidung zwischen 'Individuell'- und 'Standard'-Templates (momentan noch) nötig ist, da bspw. die 'Standard'-Templates in den Dokumenten gezielt gesucht werden und deshalb
   * auch nicht gelöscht werden sollten.
   */
  public boolean isTemplateStandard() {
    return !isTemplateIndividuell();
  }

  public boolean isTemplateIndividuell() {
    return usesTemplate(EntschaedigungsartTemplate.INDIVIDUELL);
  }

  public boolean usesTemplate(EntschaedigungsartTemplate template) {
    return template != null && equalsIgnoreCase(template.getTechnicalName(), getTemplate());
  }

  public void setTemplate(EntschaedigungsartTemplate template) {
    setTemplate(template.getTechnicalName());
  }

  protected String preProcessTemplate(String neuerWert) {
    if (isBlank(neuerWert)) {
      return EntschaedigungsartTemplate.INDIVIDUELL.getTechnicalName();
    }

    return neuerWert;
  }

  protected void preSetTemplate(String alterWert, String neuerWert) {
    // Do nothing.
  }

  protected void postSetTemplate(String alterWert, String neuerWert) {
    // Do nothing.
  }

  public boolean isKriteriumPersonendatenArtIrrelevant() {
    return usesKriteriumPersonendatenArt(EntschaedigungsartKriteriumPersonendatenArt.IRRELEVANT.getTechnicalName());
  }

  protected boolean matchesKriteriumPersonendatenArt(String kriteriumPersonendatenArt) {
    return isKriteriumPersonendatenArtIrrelevant() || usesKriteriumPersonendatenArt(kriteriumPersonendatenArt);
  }

  public boolean usesKriteriumPersonendatenArt(PersonendatenArt personendatenArt) {
    return personendatenArt != null && usesKriteriumPersonendatenArt(personendatenArt.getTechnicalName());
  }

  protected boolean usesKriteriumPersonendatenArt(String kriteriumPersonendatenArt) {
    return equalsIgnoreCase(kriteriumPersonendatenArt, getKriteriumPersonendatenArt());
  }

  public void setKriteriumPersonendatenArt(EntschaedigungsartKriteriumPersonendatenArt kriteriumPersonendatenArt) {
    setKriteriumPersonendatenArt(kriteriumPersonendatenArt.getTechnicalName());
  }

  protected String preProcessKriteriumPersonendatenArt(String neuerWert) {
    if (isBlank(neuerWert)) {
      return EntschaedigungsartKriteriumPersonendatenArt.IRRELEVANT.getTechnicalName();
    }

    return neuerWert;
  }

  protected void preSetKriteriumPersonendatenArt(String alterWert, String neuerWert) {
    // Do nothing.
  }

  protected void postSetKriteriumPersonendatenArt(String alterWert, String neuerWert) {
    // Do nothing.
  }

  public boolean isKriteriumEinsatzfunktionIrrelevant() {
    return usesKriteriumEinsatzfunktion(EntschaedigungsartKriteriumEinsatzfunktion.IRRELEVANT);
  }

  protected boolean matchesKriteriumEinsatzfunktion(String kriteriumEinsatzfunktion) {
    return isKriteriumEinsatzfunktionIrrelevant() || equalsIgnoreCase(kriteriumEinsatzfunktion, getKriteriumEinsatzfunktion());
  }

  public boolean usesKriteriumEinsatzfunktion(EntschaedigungsartKriteriumEinsatzfunktion kriteriumEinsatzfunktion) {
    return usesKriterium(this::getKriteriumEinsatzfunktion, kriteriumEinsatzfunktion);
  }

  public void setKriteriumEinsatzfunktion(EntschaedigungsartKriteriumEinsatzfunktion kriteriumEinsatzfunktion) {
    setKriteriumEinsatzfunktion(kriteriumEinsatzfunktion.getTechnicalName());
  }

  protected String preProcessKriteriumEinsatzfunktion(String neuerWert) {
    if (isBlank(neuerWert)) {
      return EntschaedigungsartKriteriumEinsatzfunktion.IRRELEVANT.getTechnicalName();
    }

    return neuerWert;
  }

  protected void preSetKriteriumEinsatzfunktion(String alterWert, String neuerWert) {
    // Do nothing.
  }

  protected void postSetKriteriumEinsatzfunktion(String alterWert, String neuerWert) {
    // Do nothing.
  }

  public boolean isKriteriumEinsatzortIrrelevant() {
    return usesKriteriumEinsatzort(EntschaedigungsartKriteriumEinsatzort.IRRELEVANT);
  }

  public boolean usesKriteriumEinsatzort(EntschaedigungsartKriteriumEinsatzort kriteriumEinsatzort) {
    return usesKriterium(this::getKriteriumEinsatzort, kriteriumEinsatzort);
  }

  protected boolean matchesKriteriumEinsatzort(String kriteriumEinsatzort) {
    return isKriteriumEinsatzortIrrelevant() || equalsIgnoreCase(kriteriumEinsatzort, getKriteriumEinsatzort());
  }

  public void setKriteriumEinsatzort(EntschaedigungsartKriteriumEinsatzort kriteriumEinsatzort) {
    setKriteriumEinsatzort(kriteriumEinsatzort.getTechnicalName());
  }

  protected String preProcessKriteriumEinsatzort(String neuerWert) {
    if (isBlank(neuerWert)) {
      return EntschaedigungsartKriteriumEinsatzort.IRRELEVANT.getTechnicalName();
    }

    return neuerWert;
  }

  protected void preSetKriteriumEinsatzort(String alterWert, String neuerWert) {
    // Do nothing.
  }

  protected void postSetKriteriumEinsatzort(String alterWert, String neuerWert) {
    // Do nothing.
  }

  private boolean usesKriterium(Supplier<String> kriteriumGetter, EntschaedigungsartKriterium kriterium) {
    return kriterium != null && equalsIgnoreCase(kriterium.getTechnicalName(), kriteriumGetter.get());
  }

  private String formatBetragMitWaehrung(Locale locale, BigDecimal betrag) {
    return formatBetragMitWaehrung(locale, betrag, getWaehrungAlsIsoCode());
  }

  public static String formatBetragMitWaehrung(Locale locale, BigDecimal betrag, String waehrungAlsIsoCode) {
    if (betrag != null && isNotBlank(waehrungAlsIsoCode)) {
      return MoneyUtil.formatBetragMitWaehrungskuerzel(locale, betrag, waehrungAlsIsoCode);
    }

    return "";
  }

  static BigDecimal roundBetrag(BigDecimal betrag) {
    if (betrag != null) {
      return betrag.setScale(2, RoundingMode.DOWN);
    }

    return betrag;
  }
}
