
package de.ivu.elect.dokumente;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;

import de.ivu.document.generator.template.annotations.Document;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentMethod;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.anschrift.entity.Anschrift;
import de.ivu.elect.business.anschrift.entity.AnschriftArt;
import de.ivu.elect.business.dokumente.entity.DokumentVorlageArt;
import de.ivu.elect.business.person.entity.Kontoverbindung;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.veranstaltung.boundary.AbstractTeilnahmeDAO;
import de.ivu.elect.business.veranstaltung.control.VeranstaltungByBeginnComparator;
import de.ivu.elect.business.veranstaltung.entity.Teilnahme;
import de.ivu.elect.business.veranstaltung.entity.Veranstaltung;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.dokumente.veranstaltung.VeranstaltungOhneTeilnehmerDocument;
import de.ivu.elect.dokumente.veranstaltung.Veranstaltungsinformationen;

@Document(value = DokumentVorlageArt.PERSON_SUB_TEMPLATE, isSubTemplate = true)
public class PersonDokument extends PersonOhneAnschriftOhneKonto {

  private static final long serialVersionUID = 1L;

  public static final PersonDokument EMPTY_OBJECT = new PersonDokument();

  @DocumentComplexField("Anschrift")
  public final AnschriftDokument anschrift;

  @DocumentSimpleField("Anschrift-Ist-Erfasst")
  public final boolean anschriftErfasst; // ELECT-11236

  @DocumentComplexField("Erreichbarkeitsanschrift")
  public final AnschriftDokument erreichbarkeitsanschrift; // ELECT-10295

  @DocumentSimpleField("Erreichbarkeitsanschrift-Ist-Erfasst")
  public final boolean erreichbarkeitsanschriftErfasst; // ELECT-11236

  /**
   * @deprecated
   * @see AnschriftDokument#strasse
   */
  @Deprecated
  @DocumentSimpleField("Straße")
  public final String strasse;

  /**
   * @deprecated
   * @see AnschriftDokument#hausnummer
   */
  @Deprecated
  @DocumentSimpleField("Hausnummer")
  public final String hausnummer;

  /**
   * @deprecated
   * @see AnschriftDokument#plz
   */
  @Deprecated
  @DocumentSimpleField("PLZ")
  public final String plz;

  /**
   * @deprecated
   * @see AnschriftDokument#ort
   */
  @Deprecated
  @DocumentSimpleField("Ort")
  public final String ort;

  /**
   * @deprecated
   * @see AnschriftDokument#ortsteil
   */
  @Deprecated
  @DocumentSimpleField("Ortsteil")
  public final String ortsteil;

  /**
   * @deprecated
   * @see AnschriftDokument#zustellanweisung
   */
  @Deprecated
  @DocumentSimpleField("Zustellanweisung")
  public final String zustellanweisung;

  /**
   * @deprecated
   * @see AnschriftDokument#strasseMitHausnummerUndZusatz
   */
  @Deprecated
  @DocumentSimpleField("StraßeMitHausNummerUndZusatz")
  public final String strasseMitHausnummerUndZusatz;

  @DocumentSimpleField("Stimmbezirk-Schluessel")
  public final String stimmbezirkSchluessel; // ELECT-9918

  @DocumentSimpleField("Finanz ID")
  public final String finanzId; // ELECTKOM-1352

  @DocumentSimpleField("Kontoinhaber")
  public final String kontoinhaber;

  @DocumentSimpleField("IBAN")
  public final String iban;

  @DocumentSimpleField("BIC")
  public final String bic;

  @DocumentSimpleField("Kreditinstitut")
  public final String kreditinstitut;

  protected PersonDokument() {
    super();

    this.anschrift = AnschriftDokument.EMPTY_OBJECT;
    this.anschriftErfasst = false;
    this.strasse = this.anschrift.strasse;
    this.hausnummer = this.anschrift.hausnummer;
    this.plz = this.anschrift.plz;
    this.ort = this.anschrift.ort;
    this.ortsteil = this.anschrift.ortsteil;
    this.zustellanweisung = this.anschrift.zustellanweisung;
    this.strasseMitHausnummerUndZusatz = this.anschrift.strasseMitHausnummerUndZusatz;

    this.erreichbarkeitsanschrift = AnschriftDokument.EMPTY_OBJECT;
    this.erreichbarkeitsanschriftErfasst = false;

    this.stimmbezirkSchluessel = "";

    this.finanzId = "";
    this.kontoinhaber = "";
    this.iban = "";
    this.bic = "";
    this.kreditinstitut = "";
  }

  public PersonDokument(BaseElectContext baseElectContext, Personendaten personendaten) {
    super(baseElectContext, personendaten);

    this.anschrift = new AnschriftDokument(baseElectContext, personendaten.getAnschrift());
    this.anschriftErfasst = isAnschriftErfasst(personendaten.getAnschrift());
    this.strasse = this.anschrift.strasse;
    this.hausnummer = this.anschrift.hausnummer;
    this.plz = this.anschrift.plz;
    this.ort = this.anschrift.ort;
    this.ortsteil = this.anschrift.ortsteil;
    this.zustellanweisung = this.anschrift.zustellanweisung;
    this.strasseMitHausnummerUndZusatz = this.anschrift.strasseMitHausnummerUndZusatz;

    Anschrift erreichbarkeitsanschrift = findErreichbarkeitsanschrift(personendaten);
    this.erreichbarkeitsanschrift = new AnschriftDokument(baseElectContext, erreichbarkeitsanschrift);
    this.erreichbarkeitsanschriftErfasst = isAnschriftErfasst(erreichbarkeitsanschrift);

    this.stimmbezirkSchluessel = personendaten.getStimmbezirkSchluessel();

    Kontoverbindung kontoverbindung = personendaten.getKontoverbindung();
    this.finanzId = personendaten.getFinanzId();
    this.kontoinhaber = kontoverbindung.getKontoinhaber();
    this.iban = kontoverbindung.getIban();
    this.bic = kontoverbindung.getBic();
    this.kreditinstitut = kontoverbindung.getKreditinstitut();
  }

  @DocumentMethod
  public AnschriftDokument getAnschriftOderErreichbarkeitsanschrift() {
    if (anschriftErfasst) {
      return anschrift;
    }

    return erreichbarkeitsanschrift;
  }

  @DocumentMethod
  public AnschriftDokument getErreichbarkeitsanschriftOderAnschrift() {
    if (erreichbarkeitsanschriftErfasst) {
      return erreichbarkeitsanschrift;
    }

    return anschrift;
  }

  private Anschrift findErreichbarkeitsanschrift(Personendaten personendaten) {
    Anschrift erreichbarkeitsanschrift = personendaten.getErreichbarkeitsanschrift();

    if (!isAnschriftErfasst(erreichbarkeitsanschrift)) {
      Anschrift anschrift = personendaten.getAnschrift();

      if (AnschriftArt.ERREICHBARKEITSANSCHRIFT.equals(anschrift.getAnschriftArt())) {
        return anschrift;
      }
    }

    return erreichbarkeitsanschrift;
  }

  private boolean isAnschriftErfasst(Anschrift anschrift) {
    return anschrift != null && Stream.of(anschrift.getStrasse(), anschrift.getOrt(), anschrift.getStaat()).anyMatch(StringUtils::isNotBlank);
  }

  protected List<Veranstaltungsinformationen> findeVeranstaltungsinformationen(BaseElectContext baseElectContext, Personendaten personendaten, AbstractTeilnahmeDAO teilnahmeDAO) {
    List<Veranstaltungsinformationen> result = new ArrayList<>();

    for (Veranstaltung veranstaltung : findeVeranstaltungen(personendaten, teilnahmeDAO)) {
      result.add(new Veranstaltungsinformationen(baseElectContext, veranstaltung));
    }

    return result;

  }

  protected List<VeranstaltungOhneTeilnehmerDocument> findeVeranstaltungenOhneTeilnehmer(BaseElectContext baseElectContext, Personendaten personendaten, AbstractTeilnahmeDAO teilnahmeDAO) {
    List<VeranstaltungOhneTeilnehmerDocument> result = new ArrayList<>();

    for (Veranstaltung veranstaltung : findeVeranstaltungen(personendaten, teilnahmeDAO)) {
      result.add(new VeranstaltungOhneTeilnehmerDocument(baseElectContext, veranstaltung));
    }

    return result;
  }

  protected List<Veranstaltung> findeVeranstaltungen(Personendaten personendaten, AbstractTeilnahmeDAO teilnahmeDAO) {
    // TODO mal beizeiten Überarbeiten und gleich Veranstaltungen aus der Datenbank holen...
    Wahltermin wahltermin = personendaten.getWahltermin();
    Wahl wahl = personendaten.getWahl();

    List<Teilnahme> teilnamhmen;

    if (wahl == null) {
      teilnamhmen = teilnahmeDAO.findByPersonAndWahltermin(personendaten, wahltermin);
    } else {
      teilnamhmen = teilnahmeDAO.findByPersonAndWahl(personendaten, wahl);
    }

    if (teilnamhmen.isEmpty()) {
      return new ArrayList<>();
    }

    Set<Veranstaltung> veranstaltungen = new HashSet<>();

    for (Teilnahme teilnahme : teilnamhmen) {
      veranstaltungen.add(teilnahme.getVeranstaltung());
    }

    ArrayList<Veranstaltung> veranstaltungenSortiert = new ArrayList<>(veranstaltungen);
    Collections.sort(veranstaltungenSortiert, new VeranstaltungByBeginnComparator());

    return veranstaltungenSortiert;
  }

  public boolean isNotEmptyObject() {
    return !isEmptyObject();
  }

  public boolean isEmptyObject() {
    return this == EMPTY_OBJECT;
  }
}
