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

import static java.util.Arrays.stream;

import java.math.BigDecimal;
import java.util.Locale;
import java.util.Objects;

import javax.persistence.Transient;

import de.ivu.elect.business.person.entity.Kontoverbindung;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.person.entity.PersonendatenArt;
import de.ivu.elect.jee.entity.EntityUtil;

public abstract class AbstractEntschaedigung implements EntschaedigungBase {

  private static final long serialVersionUID = 1L;

  @Transient
  private transient Long anzahlDefault;

  @Transient
  private transient BigDecimal einzelbetragDefault;

  public AbstractEntschaedigung() {
    super();
  }

  public boolean isBearbeitbar() {
    return !hasAuszahlung() && !getStatus().isAusgezahlt();
  }

  public boolean overridesEntschaedigungDefaults() {
    return overridesEntschaedigungAnzahlDefault() || overridesEntschaedigungEinzelbetragDefault();
  }

  private boolean overridesEntschaedigungAnzahlDefault() {
    return !Objects.equals(getAnzahlDefault(), getAnzahl());
  }

  private boolean overridesEntschaedigungEinzelbetragDefault() {
    return !(Objects.equals(getEinzelbetragDefault(), getEinzelbetrag())
        || getEinzelbetragDefault() != null && getEinzelbetrag() != null && getEinzelbetragDefault().compareTo(getEinzelbetrag()) == 0);
  }

  public boolean usesAnyEntschaedigungsartTemplate(EntschaedigungsartTemplate... templates) {
    return stream(templates).anyMatch(this::usesEntschaedigungsartTemplate);
  }

  public boolean usesEntschaedigungsartTemplate(EntschaedigungsartTemplate template) {
    return hasEntschaedigungsart() && getEntschaedigungsart().usesTemplate(template);
  }

  public String getKriteriumPersonendatenArt() {
    if (hasEntschaedigungsart()) {
      return getEntschaedigungsart().getKriteriumPersonendatenArt();
    }

    return "";
  }

  public boolean usesKriteriumPersonendatenArt(PersonendatenArt personendatenArt) {
    return hasEntschaedigungsart() && getEntschaedigungsart().usesKriteriumPersonendatenArt(personendatenArt);
  }

  public boolean isKriteriumEinsatzIrrelevant() {
    return hasEntschaedigungsart() && getEntschaedigungsart().isKriteriumEinsatzIrrelevant();
  }

  public boolean matchesKriteriumEinsatz(EntschaedigungsartKriteriumEinsatzort kriteriumEinsatzort, EntschaedigungsartKriteriumEinsatzfunktion kriteriumEinsatzfunktion) {
    return hasEntschaedigungsart() && getEntschaedigungsart().matchesKriteriumEinsatz(kriteriumEinsatzort, kriteriumEinsatzfunktion);
  }

  public boolean isKriteriumEinsatzfunktionIrrelevant() {
    return hasEntschaedigungsart() && getEntschaedigungsart().isKriteriumEinsatzfunktionIrrelevant();
  }

  public boolean isKriteriumEinsatzortIrrelevant() {
    return hasEntschaedigungsart() && getEntschaedigungsart().isKriteriumEinsatzortIrrelevant();
  }

  public boolean isBerechnetPerEinsatz() {
    return hasEntschaedigungsart() && getEntschaedigungsart().isBerechnetPerEinsatz();
  }

  public boolean isBerechnetPerAnzahl() {
    return hasEntschaedigungsart() && getEntschaedigungsart().isBerechnetPerAnzahl();
  }

  public boolean hasEntschaedigungsart() {
    return getEntschaedigungsart() != null;
  }

  public boolean hasAuszahlung() {
    return getAuszahlung() != null;
  }

  public boolean hasPersonendaten() {
    return getPersonendaten() != null;
  }

  public <T extends Personendaten> boolean hasPersonendaten(Class<T> clazz) {
    return hasPersonendaten() && getPersonendaten().isInstanceOf(clazz);
  }

  public <T extends Personendaten> T getPersonendaten(Class<T> clazz) {
    if (hasPersonendaten(clazz)) {
      return EntityUtil.castTo(getPersonendaten(), clazz);
    }

    return null;
  }

  public boolean hasKontoverbindung() {
    return getKontoverbindung() != null;
  }

  public Kontoverbindung getKontoverbindung() {
    if (hasPersonendaten()) {
      return getPersonendaten().getKontoverbindung();
    }

    return null;
  }

  public Long getAnzahlDefault() {
    if (anzahlDefault == null && hasEntschaedigungsart()) {
      return getEntschaedigungsart().getAnzahlInitial();
    }

    return anzahlDefault;
  }

  public void setAnzahlDefault(Long anzahlDefault) {
    this.anzahlDefault = anzahlDefault;
  }

  public boolean hasAnzahl() {
    return getAnzahl() != null;
  }

  public boolean usesAnzahl(Long anzahl) {
    return isSameEntschaedigungDetail(getAnzahl(), anzahl);
  }

  public BigDecimal getEinzelbetragDefault() {
    if (einzelbetragDefault == null && hasEntschaedigungsart()) {
      return getEntschaedigungsart().getEinzelbetragInitial();
    }

    return einzelbetragDefault;
  }

  public void setEinzelbetragDefault(BigDecimal einzelbetragDefault) {
    this.einzelbetragDefault = einzelbetragDefault;
  }

  public boolean hasEinzelbetrag() {
    return getEinzelbetrag() != null;
  }

  public String getEinzelbetragMitWaehrung(Locale locale) {
    return formatBetragMitWaehrung(locale, getEinzelbetrag());
  }

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

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

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

  public boolean hasGesamtbetragNonZero() {
    return hasGesamtbetrag() && getGesamtbetrag().compareTo(BigDecimal.ZERO) > 0;
  }

  public boolean hasGesamtbetrag() {
    return getGesamtbetrag() != null;
  }

  public String getGesamtbetragMitWaehrung(Locale locale) {
    return formatBetragMitWaehrung(locale, getGesamtbetrag());
  }

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

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

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

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

  public boolean usesSameEntschaedigungDetails(Entschaedigung another) {
    if (another == null //
        || !isSameEntschaedigungDetail(getPersonendaten(), another.getPersonendaten()) //
        || !isSameEntschaedigungDetail(getEntschaedigungsart(), another.getEntschaedigungsart()) //
        || !isSameEntschaedigungDetail(getAnzahl(), another.getAnzahl()) //
        || !isSameEntschaedigungDetail(getEinzelbetrag(), another.getEinzelbetrag()) //
        || !isSameEntschaedigungDetail(getGesamtbetrag(), another.getGesamtbetrag()) //
        || !isSameEntschaedigungDetail(getWaehrungAlsIsoCode(), another.getWaehrungAlsIsoCode())) {
      return false;
    }

    return true;
  }

  private boolean isSameEntschaedigungDetail(Object one, Object another) {
    return Objects.equals(one, another);
  }

  private boolean isSameEntschaedigungDetail(BigDecimal one, BigDecimal another) {
    return Objects.equals(one, another) || one != null && another != null && one.compareTo(another) == 0;
  }

  public void copyEntschaedigungDetailsFrom(Entschaedigung entschaedigung) {
    if (entschaedigung != null) {
      entschaedigung.copyEntschaedigungDetailsTo((Entschaedigung) this);
    }
  }

  public void copyEntschaedigungDetailsTo(Entschaedigung entschaedigung) {
    copyEntityAttributesToOtherEntity(entschaedigung);
  }

  private static String formatBetragMitWaehrung(Locale locale, BigDecimal betrag, String waehrungAlsIsoCode) {
    return AbstractEntschaedigungsart.formatBetragMitWaehrung(locale, betrag, waehrungAlsIsoCode);
  }

  private static BigDecimal roundBetrag(BigDecimal betrag) {
    return AbstractEntschaedigungsart.roundBetrag(betrag);
  }
}
