
package de.ivu.elect.dokumente.ergebnis.gesamtergebnis;

import static java.util.stream.Collectors.toMap;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;

import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlartErgebnisart;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStimmzettelPosition;
import de.ivu.elect.dokumente.ergebnis.AbstractErgebnisTabellarischDocumentZeileCreator;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungDirektkandidatur;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungEingabe;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungGruppe;
import de.ivu.elect.was.exchange.ergebnis.zusammenstellung.ErgebnisZusammenstellungListe;

public class ErgebnisGesamtergebnisTabellarischDocumentZeileCreator extends AbstractErgebnisTabellarischDocumentZeileCreator<ErgebnisGesamtergebnisTabellarischDocumentZeile> {

  public ErgebnisGesamtergebnisTabellarischDocumentZeileCreator() {
    super();
  }

  @Override
  protected ErgebnisGesamtergebnisTabellarischDocumentZeile createZeile(WahlartErgebnisart ergebnisart, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return new ErgebnisGesamtergebnisTabellarischDocumentZeile(ergebnisart, wahldurchfuehrungsgebiet);
  }

  @Override
  protected void fillInErgebnisDatenGueltige(Wahl wahl, ErgebnisGesamtergebnisTabellarischDocumentZeile zeile, Long anzahlStimmzettelGueltig, Long anzahlStimmzettelUngueltig,
      Long anzahlStimmzettelUnbestimmt, Long anzahlStimmzettelGesamt, Long direktstimmenGueltig, Long direktstimmenUngueltig, Long direktstimmenGesamt, Long listenstimmenGueltig,
      Long listenstimmenUngueltig, Long listenstimmenGesamt) {
    super.fillInErgebnisDatenGueltige(wahl, zeile, anzahlStimmzettelGueltig, anzahlStimmzettelUngueltig, anzahlStimmzettelUnbestimmt, anzahlStimmzettelGesamt, direktstimmenGueltig,
        direktstimmenUngueltig, direktstimmenGesamt, listenstimmenGueltig, listenstimmenUngueltig, listenstimmenGesamt);

    zeile.erststimmenGueltig = direktstimmenGueltig;
    zeile.erststimmenUngueltig = direktstimmenUngueltig;
    zeile.erststimmenGesamt = direktstimmenGesamt;

    zeile.zweitstimmenGueltig = listenstimmenGueltig;
    zeile.zweitstimmenUngueltig = listenstimmenUngueltig;
    zeile.zweitstimmenGesamt = listenstimmenGesamt;
  }

  @Override
  protected void fillInErgebnisDatenStimmen(Wahl wahl, ErgebnisGesamtergebnisTabellarischDocumentZeile zeile, List<ErgebnisZusammenstellungDirektkandidatur> direktkandidaturen,
      List<ErgebnisZusammenstellungListe> listen) {
    super.fillInErgebnisDatenStimmen(wahl, zeile, direktkandidaturen, listen);

    Map<Long, ErgebnisZusammenstellungListe> listePerStimmzettelPosition = listen.stream() //
        .filter(ErgebnisZusammenstellungListe::hasStimmzettelPosition) //
        .collect(toMap(wahlvorschlag -> wahlvorschlag.getStimmzettelPosition().getPosition(), Function.identity()));

    Map<Long, ErgebnisZusammenstellungDirektkandidatur> direktkandidaturPerStimmzettelPosition = direktkandidaturen.stream() //
        .filter(ErgebnisZusammenstellungDirektkandidatur::hasStimmzettelPosition) //
        .collect(toMap(wahlvorschlag -> wahlvorschlag.getStimmzettelPosition().getPosition(), Function.identity()));

    SortedSet<Long> stimmzettelPositionen = new TreeSet<>();
    stimmzettelPositionen.addAll(listePerStimmzettelPosition.keySet());
    stimmzettelPositionen.addAll(direktkandidaturPerStimmzettelPosition.keySet());

    for (Long stimmzettelPosition : stimmzettelPositionen) {
      Optional<ErgebnisZusammenstellungListe> liste = Optional.ofNullable(listePerStimmzettelPosition.get(stimmzettelPosition));
      Optional<ErgebnisZusammenstellungDirektkandidatur> direktkandidatur = Optional.ofNullable(direktkandidaturPerStimmzettelPosition.get(stimmzettelPosition));

      Optional<ErgebnisZusammenstellungGruppe> gruppeIfAnyWahlvorschlag = liste.map(ErgebnisZusammenstellungListe::getGruppe)
          .or(() -> direktkandidatur.map(ErgebnisZusammenstellungDirektkandidatur::getGruppe));

      if (gruppeIfAnyWahlvorschlag.isPresent()) {
        Optional<WahlvorschlagStimmzettelPosition> listeStimmzettelPosition = liste.map(ErgebnisZusammenstellungListe::getStimmzettelPosition);
        String listeKurznameOderKennwort = liste.map(ErgebnisZusammenstellungListe::getKurznameOderKennwort).orElse("");

        Optional<WahlvorschlagStimmzettelPosition> direktkandidaturStimmzettelPosition = direktkandidatur.map(ErgebnisZusammenstellungDirektkandidatur::getStimmzettelPosition);

        ErgebnisZusammenstellungGruppe gruppe = gruppeIfAnyWahlvorschlag.get();
        Optional<WahlvorschlagStimmzettelPosition> gruppeStimmzettelPosition = listeStimmzettelPosition.or(() -> direktkandidaturStimmzettelPosition);

        String gruppeKurzname = gruppe.getKurzname();

        if (gruppe.getGruppeArt().isEinzelbewerber()) {
          gruppeKurzname = getMessage("ergebnis.gesamtergebnis.tabellarisch.exportieren.dokument.zeile.einzelbewerber.kurzname", gruppeKurzname);
        }

        String gruppeKurznameOderKennwort = defaultIfBlank(listeKurznameOderKennwort, direktkandidatur.map(ErgebnisZusammenstellungDirektkandidatur::getKurznameOderKennwort).orElse(""));

        zeile.parteiStimmenQueueNummer.add(gruppe.getSchluessel());
        zeile.parteiStimmenQueueNameGruppe.add(gruppeKurzname);
        zeile.parteiStimmenQueueNameWahlvorschlag.add(gruppeKurznameOderKennwort);
        zeile.parteiStimmenQueueStimmzettelPositionWahlvorschlag.add(gruppeStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::getPosition).orElse(null));
        zeile.parteiStimmenQueueKennung.add("D" + gruppeStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::formatPosition).orElse("")); // LEGACY!!!

        if (direktkandidatur.isPresent()) {
          zeile.parteiStimmenQueueKennungErststimmen.add("D" + direktkandidaturStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::formatPosition).orElse(""));
          zeile.parteiStimmenQueueKennungPositionErststimmen.add(direktkandidaturStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::formatPosition).orElse(""));
          zeile.parteiStimmenQueueAnzahlErststimmen.add(direktkandidatur.map(ErgebnisZusammenstellungDirektkandidatur::getStimmenGesamt).map(ErgebnisZusammenstellungEingabe::getAnzahl).orElse(null));
          zeile.parteiStimmenQueueNameGruppeErststimmen.add(gruppeKurzname);
          zeile.parteiStimmenQueueNameDirektkandidat.add(direktkandidatur.map(this::formatKandidaturName).orElse(""));
          zeile.parteiStimmenQueueNameDirektkandidatVollstaendig.add(direktkandidatur.map(this::formatKandidaturNameVollstaendig).orElse(""));
          zeile.parteiStimmenQueueNameDirektkandidatVorname.add(direktkandidatur.map(this::formatKandidaturVornamen).orElse(""));
        }

        if (liste.isPresent()) {
          zeile.parteiStimmenQueueKennungZweitstimmen.add("F" + listeStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::formatPosition).orElse(""));
          zeile.parteiStimmenQueueKennungPositionZweitstimmen.add(listeStimmzettelPosition.map(WahlvorschlagStimmzettelPosition::formatPosition).orElse(""));
          zeile.parteiStimmenQueueAnzahlZweitstimmen.add(liste.map(ErgebnisZusammenstellungListe::getStimmenGesamt).map(ErgebnisZusammenstellungEingabe::getAnzahl).orElse(null));
          zeile.parteiStimmenQueueNameGruppeZweitstimmen.add(gruppeKurzname);
          zeile.parteiStimmenQueueNameListe.add(listeKurznameOderKennwort);
        }
      }
    }
  }

  /*
   *
   *
   *
   *
   *
   * ELECT-15337, LEGACY-Methoden -> NICHT mehr erweitern!!!
   *
   *
   *
   *
   *
   */

  @Override
  protected void fillInErgebnisDatenStimmen(ErgebnisGesamtergebnisTabellarischDocumentZeile zeile, ErgebnisDaten ergebnisDaten, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
      Stimmabgabeart stimmabgabeart) {
    EntityAufStimmzettelDaten entityAufStimmzettelDaten = ergebnisDaten.getEntityAufStimmzettelDaten();

    GebietUndStimmabgabeart gebietUndStimmabgabeart = new GebietUndStimmabgabeart(wahldurchfuehrungsgebiet, stimmabgabeart);

    for (PolitischeGruppe politischeGruppe : collectPolitischeGruppenRelevantSorted(ergebnisDaten, wahldurchfuehrungsgebiet)) {
      GruppeImGebiet gruppeImGebiet = ergebnisDaten.getGruppeImGebiet(politischeGruppe, wahldurchfuehrungsgebiet);
      Wahlvorschlag wahlvorschlag = gruppeImGebiet.getErstenWahlvorschlagAbgeleitetMitStimmzettelPosition(entityAufStimmzettelDaten);

      if (wahlvorschlag != null) {
        String gruppeKurzname = politischeGruppe.getKurznameAufGebiet(wahldurchfuehrungsgebiet);

        if (politischeGruppe.isEinzelbewerber()) {
          gruppeKurzname = getMessage("ergebnis.gesamtergebnis.tabellarisch.exportieren.dokument.zeile.einzelbewerber.kurzname", gruppeKurzname);
        }

        WahlvorschlagStimmzettelPosition wahlvorschlagStimmzettelPosition = wahlvorschlag.getStimmzettelPosition(wahldurchfuehrungsgebiet, entityAufStimmzettelDaten);

        zeile.parteiStimmenQueueNummer.add(politischeGruppe.getSchluessel());
        zeile.parteiStimmenQueueNameGruppe.add(gruppeKurzname);
        zeile.parteiStimmenQueueNameWahlvorschlag.add(wahlvorschlag.getKurznameOderKennwort());
        zeile.parteiStimmenQueueStimmzettelPositionWahlvorschlag.add(wahlvorschlagStimmzettelPosition.getPosition());
        zeile.parteiStimmenQueueKennung.add("D" + wahlvorschlagStimmzettelPosition.formatPosition()); // LEGACY!!!

        Direktkandidatur direktkandidatur = extractDirektkandidatur(wahldurchfuehrungsgebiet, ergebnisDaten, politischeGruppe, wahlvorschlag);

        if (direktkandidatur != null) { // ELECT-12398
          String direktkandidaturFamilienname = direktkandidatur.getFamilienname();
          Long anzahlDirektstimmen = ergebnisDaten.getErststimmenAktuell(politischeGruppe, gebietUndStimmabgabeart);

          zeile.parteiStimmenQueueKennungErststimmen.add("D" + wahlvorschlagStimmzettelPosition.formatPosition());
          zeile.parteiStimmenQueueKennungPositionErststimmen.add(wahlvorschlagStimmzettelPosition.formatPosition());
          zeile.parteiStimmenQueueAnzahlErststimmen.add(anzahlDirektstimmen);
          zeile.parteiStimmenQueueNameGruppeErststimmen.add(gruppeKurzname);
          zeile.parteiStimmenQueueNameDirektkandidat.add(direktkandidaturFamilienname);
          zeile.parteiStimmenQueueNameDirektkandidatVollstaendig
              .add(formatKandidaturNameVollstaendig(direktkandidatur.getTitel(), direktkandidatur.getFamiliennameBestandteil(), direktkandidaturFamilienname));
          zeile.parteiStimmenQueueNameDirektkandidatVorname.add(defaultIfBlank(direktkandidatur.getRufname(), direktkandidatur.getVornamen()));
        }

        Liste liste = extractListe(wahldurchfuehrungsgebiet, ergebnisDaten, politischeGruppe, wahlvorschlag);

        if (liste != null) { // ELECT-12398
          Long anzahlListenstimmen = ergebnisDaten.getZweitstimmenAktuell(politischeGruppe, gebietUndStimmabgabeart);

          zeile.parteiStimmenQueueKennungZweitstimmen.add("F" + wahlvorschlagStimmzettelPosition.formatPosition());
          zeile.parteiStimmenQueueKennungPositionZweitstimmen.add(wahlvorschlagStimmzettelPosition.formatPosition());
          zeile.parteiStimmenQueueAnzahlZweitstimmen.add(anzahlListenstimmen);
          zeile.parteiStimmenQueueNameGruppeZweitstimmen.add(gruppeKurzname);
          zeile.parteiStimmenQueueNameListe.add(liste.getKurznameOderKennwort());
        }
      }
    }
  }
}
