
package de.ivu.elect.dokumente.wahlvorschlag;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentRepeat;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.ListenkandidaturStimmzettelPosition;
import de.ivu.elect.business.pruefung.boundary.ZulassungPruefer;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Listenverbindung;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlagsperson.control.WahlvorschlagsPersonByEingangssequenznummerComparator;
import de.ivu.elect.business.wahlvorschlagsperson.entity.WahlvorschlagsPerson;
import de.ivu.elect.business.wahlvorschlagsperson.entity.WahlvorschlagsPersonTyp;
import de.ivu.elect.dokumente.CoreElectContext;
import de.ivu.elect.dokumente.PersonDokument;
import de.ivu.elect.dokumente.kandidatur.DirektkandidaturDokument;
import de.ivu.elect.dokumente.kandidatur.KandidaturCoreDaten;
import de.ivu.elect.dokumente.kandidatur.KandidaturOhnePruefungenDokument;

public abstract class AbstractWahlvorschlagsdatenDocument extends CoreWahlvorschlagsdatenDocument {

  private static final long serialVersionUID = 1L;

  private final WahlvorschlagsPersonByEingangssequenznummerComparator wahlvorschlagsPersonByEingangssequenznummerComparator = new WahlvorschlagsPersonByEingangssequenznummerComparator();

  protected final Map<Listenkandidatur, KandidaturOhnePruefungenDokument> konvertierteListenkandidaturenOhnePruefungen;

  protected final Map<Direktkandidatur, DirektkandidaturDokument> konvertierteDirektkandidaturen;

  @DocumentComplexField("Einreicher")
  public final PersonDokument einreicher;

  @DocumentSimpleField("EinreicherVorhanden")
  public final boolean einreicherVorhanden;

  @DocumentComplexField("Beauftragter")
  public final PersonDokument beauftragter;

  @DocumentSimpleField("BeauftragterVorhanden")
  public final boolean beauftragterVorhanden;

  @DocumentComplexField("BeauftragterStellvertretend")
  public final PersonDokument beauftragterStellvertretend;

  @DocumentSimpleField("BeauftragterStellvertretendVorhanden")
  public final boolean beauftragterStellvertretendVorhanden;

  @DocumentComplexField("AufstellungsversammlungsLeiter")
  public final PersonDokument aufstellungsversammlungsLeiter;

  @DocumentSimpleField("AufstellungsversammlungsLeiterVorhanden")
  public final boolean aufstellungsversammlungsLeiterVorhanden;

  @DocumentRepeat(value = "Unterzeichner", elementClass = PersonDokument.class, itemName = "person")
  public final List<PersonDokument> unterzeichner;

  @DocumentSimpleField("UnterzeichnerVorhanden")
  public final boolean unterzeichnerVorhanden;

  @DocumentRepeat(value = "AufstellungsversammlungMitunterzeichner", elementClass = PersonDokument.class, itemName = "person")
  public final List<PersonDokument> aufstellungsversammlungMitunterzeichner;

  @DocumentSimpleField("AufstellungsversammlungMitunterzeichnerVorhanden")
  public final boolean aufstellungsversammlungMitunterzeichnerVorhanden;

  @DocumentComplexField("Direktkandidatur")
  public final DirektkandidaturDokument direktkandidatur;

  @DocumentRepeat(value = "AlleVerweigerungsgruendeNichtZulassung", elementClass = VerweigerungsGrundZulassungDocument.class, itemName = "verweigerungsgrund")
  public final List<VerweigerungsGrundZulassungDocument> alleVerweigerungsgruendeNichtZulassung;

  @DocumentSimpleField("AlleVerweigerungsgruendeNichtZulassungVorhanden")
  public final boolean alleVerweigerungsgruendeNichtZulassungVorhanden;

  @DocumentSimpleField("VollstaendigZugelassenOderZulassungVollstaendigErlaubt")
  public final boolean vollstaendigZugelassenOderZulassungVollstaendigErlaubt;

  @DocumentSimpleField("ZugelassenOderZulassungErlaubt")
  public final boolean zugelassenOderZulassungErlaubt;

  @DocumentSimpleField("EingereichteListenverbindungen")
  public final String eingereichteListenverbindungen;

  @DocumentSimpleField("InListenverbindung")
  public final boolean inListenverbindung;

  @DocumentSimpleField("Listenverbindung-ID")
  public final Long listenverbindungId;

  @DocumentSimpleField("Listenverbindung-Bezeichnung")
  public final String listenverbindungBezeichnung;

  @DocumentSimpleField("LetzterInListenverbindung")
  public final boolean letzterInListenverbindung;

  @DocumentSimpleField("SindUnterstützerErforderlich")
  public final boolean unterstuetzerErforderlich;

  @DocumentSimpleField("UnterstützerErfassbarAb")
  public final Date unterstuetzerErfassbarAb;

  @DocumentSimpleField("UnterstützerErfassbarAbFormatiert")
  public final String unterstuetzerErfassbarAbFormatiert;

  @DocumentSimpleField("AnzahlGültigerUnterstützerUnterschriften")
  public final Long anzahlGueltigerUnterstuetzerUnterschriften;

  @DocumentSimpleField("AnzahlUngültigerUnterstützerUnterschriften")
  public final Long anzahlUngueltigerUnterstuetzerUnterschriften;

  @DocumentSimpleField("AnzahlUnterstützerUnterschriftenInsgesamt")
  public final Long anzahlUnterstuetzerUnterschriftenInsgesamt;

  @DocumentSimpleField("IstMindestanzahlUnterstuetzerErreicht")
  public final boolean istMindestanzahlUnterstuetzerErreicht;

  protected AbstractWahlvorschlagsdatenDocument(CoreElectContext coreElectContext, Gebiet gebiet, WahlvorschlagCoreDaten wahlvorschlagCoreDaten, Wahlvorschlag wahlvorschlag, Long laufendeNummer) {
    super(coreElectContext, wahlvorschlagCoreDaten.getEntityAufStimmzettelDaten(), gebiet, wahlvorschlag, laufendeNummer);

    ZulassungPruefer zulassungPruefer = wahlvorschlagCoreDaten.getZulassungPruefer();

    Collection<WahlvorschlagsPerson> wahlvorschlagsPersonen = wahlvorschlagCoreDaten.getWahlvorschlagsPersonen(wahlvorschlag);

    this.einreicher = bestimmeWahlvorschlagsperson(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.EINREICHER);
    this.einreicherVorhanden = this.einreicher.isNotEmptyObject();

    this.beauftragter = bestimmeWahlvorschlagsperson(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.BEAUFTRAGTER);
    this.beauftragterVorhanden = this.beauftragter.isNotEmptyObject();

    this.beauftragterStellvertretend = bestimmeWahlvorschlagsperson(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.STELLVERTRETENDER_BEAUFTRAGTER);
    this.beauftragterStellvertretendVorhanden = this.beauftragterStellvertretend.isNotEmptyObject();

    this.aufstellungsversammlungsLeiter = bestimmeWahlvorschlagsperson(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.AUFSTELLUNGSVERSAMMLUNG_LEITER);
    this.aufstellungsversammlungsLeiterVorhanden = this.aufstellungsversammlungsLeiter.isNotEmptyObject();

    this.unterzeichner = bestimmeWahlvorschlagspersonen(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.UNTERZEICHNER);
    this.unterzeichnerVorhanden = !this.unterzeichner.isEmpty();

    this.aufstellungsversammlungMitunterzeichner = bestimmeWahlvorschlagspersonen(coreElectContext, wahlvorschlagsPersonen, WahlvorschlagsPersonTyp.AUFSTELLUNGSVERSAMMLUNG_MITUNTERZEICHNER);
    this.aufstellungsversammlungMitunterzeichnerVorhanden = !this.aufstellungsversammlungMitunterzeichner.isEmpty();

    this.zugelassenOderZulassungErlaubt = this.zugelassen || zulassungPruefer.isZulassenErlaubt(wahlvorschlag);

    this.alleVerweigerungsgruendeNichtZulassung = new ArrayList<>();

    for (String grund : zulassungPruefer.getGruendeFuerVerweigerungDerZulassung(wahlvorschlag)) {
      this.alleVerweigerungsgruendeNichtZulassung.add(new VerweigerungsGrundZulassungDocument(grund));
    }

    this.alleVerweigerungsgruendeNichtZulassungVorhanden = !this.alleVerweigerungsgruendeNichtZulassung.isEmpty();

    this.konvertierteDirektkandidaturen = new HashMap<>();

    if (this.isDirektkandidat) {
      Direktkandidatur direktkandidaturEntity = wahlvorschlag.castTo(Direktkandidat.class).getEntity().getDirektkandidatur();
      // NOTE: nur das CoreWahlvorschlagsdatenDocument reingeben; das enthält keinen Backlink auf die Direktkandidatur (-> keine Endlosschleife)
      this.direktkandidatur = new DirektkandidaturDokument(coreElectContext, new CoreWahlvorschlagsdatenDocument(coreElectContext, this), wahlvorschlagCoreDaten.getKandidaturCoreDaten(),
          direktkandidaturEntity);
      this.konvertierteDirektkandidaturen.put(direktkandidaturEntity, this.direktkandidatur);
    } else {
      this.direktkandidatur = DirektkandidaturDokument.EMPTY_OBJECT;
    }

    if (this.isListe) {
      Liste liste = wahlvorschlag.castTo(Liste.class).getEntity();

      this.konvertierteListenkandidaturenOhnePruefungen = konvertiereListenkandidaturenOhnePruefungen(coreElectContext, liste, wahlvorschlagCoreDaten.getKandidaturCoreDaten());

      boolean alleListenkandidaturenZugelassenOderZulassungErlaubt = true;

      for (KandidaturOhnePruefungenDokument listenkandidaturOhnePruefungenDokument : this.konvertierteListenkandidaturenOhnePruefungen.values()) {
        if (!listenkandidaturOhnePruefungenDokument.zugelassenOderZulassungErlaubt) {
          alleListenkandidaturenZugelassenOderZulassungErlaubt = false;
        }
      }

      this.vollstaendigZugelassenOderZulassungVollstaendigErlaubt = this.zugelassenOderZulassungErlaubt && alleListenkandidaturenZugelassenOderZulassungErlaubt;
    } else {
      this.konvertierteListenkandidaturenOhnePruefungen = new HashMap<>();
      this.vollstaendigZugelassenOderZulassungVollstaendigErlaubt = zugelassenOderZulassungErlaubt;
    }

    if (this.isListe) {
      Liste liste = wahlvorschlag.castTo(Liste.class).getEntity();

      this.eingereichteListenverbindungen = liste.getListenverbindungEingereicht();

      Listenverbindung listenverbindung = liste.getListenverbindung();

      if (listenverbindung != null) {
        this.inListenverbindung = true;

        this.listenverbindungId = listenverbindung.getId();
        this.listenverbindungBezeichnung = listenverbindung.getBezeichnung();

        List<Liste> listenSortiertNachPosition = listenverbindung.getListenSortiertNachPosition();

        this.letzterInListenverbindung = listenSortiertNachPosition.indexOf(liste) == listenSortiertNachPosition.size() - 1;
      } else {
        this.inListenverbindung = false;

        this.listenverbindungId = null;
        this.listenverbindungBezeichnung = "";

        this.letzterInListenverbindung = false;
      }
    } else {
      this.eingereichteListenverbindungen = null;

      this.inListenverbindung = false;

      this.listenverbindungId = null;
      this.listenverbindungBezeichnung = "";

      this.letzterInListenverbindung = false;
    }

    this.unterstuetzerErforderlich = wahlvorschlag.isUnterstuetzerErforderlich(coreElectContext.getWahlvorschlagUnterstuetzerNotwendigErmittler());

    this.unterstuetzerErfassbarAb = wahlvorschlag.getUnterstuetzerErfassbarAbOrDefault();
    this.unterstuetzerErfassbarAbFormatiert = coreElectContext.formatDateMedium(this.unterstuetzerErfassbarAb);

    this.anzahlGueltigerUnterstuetzerUnterschriften = wahlvorschlagCoreDaten.getAnzahlUnterstuetzerGeeignetGesamt(wahlvorschlag);
    this.anzahlUngueltigerUnterstuetzerUnterschriften = wahlvorschlagCoreDaten.getAnzahlUnterstuetzerNichtGeeignetGesamt(wahlvorschlag);
    this.anzahlUnterstuetzerUnterschriftenInsgesamt = this.anzahlGueltigerUnterstuetzerUnterschriften + this.anzahlUngueltigerUnterstuetzerUnterschriften;

    Long anzahlUnterstuetzerErforderlich = wahlvorschlag.getGebiet().getAnzahlErforderlicherUnterstuetzer();

    if (anzahlUnterstuetzerErforderlich != null) {
      this.istMindestanzahlUnterstuetzerErreicht = anzahlGueltigerUnterstuetzerUnterschriften >= anzahlUnterstuetzerErforderlich;
    } else {
      if (this.unterstuetzerErforderlich) {
        this.istMindestanzahlUnterstuetzerErreicht = false;
      } else {
        this.istMindestanzahlUnterstuetzerErreicht = true;
      }
    }
  }

  private PersonDokument bestimmeWahlvorschlagsperson(CoreElectContext coreElectContext, Collection<WahlvorschlagsPerson> wahlvorschlagsPersons, WahlvorschlagsPersonTyp typ) {
    for (WahlvorschlagsPerson wahlvorschlagsPerson : wahlvorschlagsPersons) {
      if (typ.equals(wahlvorschlagsPerson.getWahlvorschlagsPersonTyp()) && !wahlvorschlagsPerson.isPersonStatusNichtGeeignet()) {
        return new PersonDokument(coreElectContext, wahlvorschlagsPerson);
      }
    }

    return PersonDokument.EMPTY_OBJECT;
  }

  private List<PersonDokument> bestimmeWahlvorschlagspersonen(CoreElectContext coreElectContext, Collection<WahlvorschlagsPerson> wahlvorschlagsPersonenUnsortiert, WahlvorschlagsPersonTyp typ) {
    List<PersonDokument> result = new ArrayList<>();

    List<WahlvorschlagsPerson> wahlvorschlagsPersonenSortiert = new ArrayList<>(wahlvorschlagsPersonenUnsortiert);
    Collections.sort(wahlvorschlagsPersonenSortiert, wahlvorschlagsPersonByEingangssequenznummerComparator);

    for (WahlvorschlagsPerson wahlvorschlagsPerson : wahlvorschlagsPersonenSortiert) {
      if (typ.equals(wahlvorschlagsPerson.getWahlvorschlagsPersonTyp()) && !wahlvorschlagsPerson.isPersonStatusNichtGeeignet()) {
        result.add(new PersonDokument(coreElectContext, wahlvorschlagsPerson));
      }
    }

    return result;
  }

  protected Map<Listenkandidatur, KandidaturOhnePruefungenDokument> konvertiereListenkandidaturenOhnePruefungen(CoreElectContext coreElectContext, Liste liste,
      KandidaturCoreDaten kandidaturCoreDaten) {
    Map<Listenkandidatur, KandidaturOhnePruefungenDokument> result = new HashMap<>();

    Long laufendeNummer = 0L;

    for (Listenkandidatur listenkandidatur : getListenkandidaturenNachStandardSortierung(liste)) {
      laufendeNummer++;

      result.put(listenkandidatur, new KandidaturOhnePruefungenDokument(coreElectContext, this, kandidaturCoreDaten, listenkandidatur, laufendeNummer));
    }

    return result;
  }

  protected List<Listenkandidatur> getListenkandidaturenNachStandardSortierung(Liste liste) {
    return liste.getListenkandidaturenSortiertNachRelativerPosition();
  }

  protected List<Listenkandidatur> getListenkandidaturenNachStimmzettelSortierung(Liste liste, EntityAufStimmzettelDaten entityAufStimmzettelDaten) {
    Collection<Listenkandidatur> listenkandidatursByListe = liste.getListenkandidaturen();

    Map<Listenkandidatur, Long> listenkandidaturenNachStimmzettelPosition = new HashMap<>();

    for (Listenkandidatur listenkandidatur : listenkandidatursByListe) {
      ListenkandidaturStimmzettelPosition listenkandidaturStimmzettelPosition = listenkandidatur.getStimmzettelPosition(entityAufStimmzettelDaten);

      if (listenkandidaturStimmzettelPosition.hasPosition()) {
        listenkandidaturenNachStimmzettelPosition.put(listenkandidatur, listenkandidaturStimmzettelPosition.getPosition());
      }
    }

    List<Listenkandidatur> result = new ArrayList<>(listenkandidaturenNachStimmzettelPosition.keySet());
    Collections.sort(result, (o1, o2) -> listenkandidaturenNachStimmzettelPosition.get(o1).compareTo(listenkandidaturenNachStimmzettelPosition.get(o2)));

    return result;
  }
}
