package de.ivu.elect.dokumente.ergebnis;

import de.ivu.document.generator.template.BaseDocument;
import de.ivu.document.generator.template.annotations.Document;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentQueue;
import de.ivu.document.generator.template.annotations.DocumentRepeat;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.dokumente.entity.NlDokumentVorlageArt;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebniskonflikt.entity.ErgebnisKonflikt;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.sitzberechnung.entity.OsvLegacyArtikelnummer;
import de.ivu.elect.business.sitzberechnung.entity.OsvLegacyKonfliktCustomData;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.dokumente.kandidatur.CoreKandidaturDokument;
import de.ivu.elect.dokumente.wahlvorschlag.CoreWahlvorschlagsdatenDocument;
import de.ivu.elect.dokumente.wahlvorschlag.GruppeDokument;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis.KandidaturMitStimmergebnissenDocument;
import de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis.ListeMitStimmergebnissenDocument;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;

@Document(value = NlDokumentVorlageArt.LOS_TEMPLATE, isSubTemplate = true)
public class NlErgebnisKonfliktDocument extends BaseDocument {

  private static final long serialVersionUID = 1L;

  @DocumentSimpleField("NL-Artikelnummer-Der-Konflikts")
  public final String artikelnummer;
  @DocumentQueue(value = "VerfuegbareLosoptionen", elementClass = String.class)
  public final LinkedList<String> verfuegbareLosoptionen;
  @DocumentSimpleField("GelosteLosoption")
  public final String losoptionGelost;
  @DocumentSimpleField("Beschreibung-des-Konflikts")
  public final String beschreibung;
  @DocumentSimpleField("PositionDesKonflikts")
  public final Long position;
  @DocumentRepeat(value = "BeteiligteListen", elementClass = CoreWahlvorschlagsdatenDocument.class, itemName = "liste")
  public final List<CoreWahlvorschlagsdatenDocument> beteiligteP2Listen = new ArrayList<>();
  @DocumentRepeat(value = "BeteiligteGruppen", elementClass = GruppeDokument.class, itemName = "gruppe")
  public final List<GruppeDokument> beteiligteGruppen = new ArrayList<>();
  @DocumentRepeat(value = "BeteiligteKandidaturen", elementClass = CoreKandidaturDokument.class, itemName = "kandidatur")
  public final List<CoreKandidaturDokument> beteiligteKandidaturen = new ArrayList<>();
  @DocumentComplexField(value = "GelosteListe")
  public final CoreWahlvorschlagsdatenDocument gelosteListe;
  @DocumentComplexField(value = "GelosteGruppe")
  public final GruppeDokument gelosteGruppe;
  @DocumentComplexField(value = "GelosteKandidatur")
  public final CoreKandidaturDokument gelosteKandidatur;

  private final OsvLegacyArtikelnummer osvLegacyArtikelnummer;

  public NlErgebnisKonfliktDocument(ErgebnisDaten ergebnisDaten, ErgebnisKonflikt ergebnisKonflikt, Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe,
                                    Map<Kandidatur, KandidaturMitStimmergebnissenDocument> ergebnisDokumentPerKandidatur, Messages messages) {
    super();

    EntityAufStimmzettelDaten entityAufStimmzettelDaten = ergebnisDaten.getEntityAufStimmzettelDaten();

    OsvLegacyKonfliktCustomData customData = ergebnisKonflikt.getCustomData(OsvLegacyKonfliktCustomData.class);

    position = customData.getPosition();

    artikelnummer = customData.getArtikelnummer().name();
    osvLegacyArtikelnummer = customData.getArtikelnummer();

    List<Kandidatur> kandidaturen = ergebnisDaten.getKandidaturen();
    List<PolitischeGruppe> politischeGruppen = ergebnisDaten.getPolitischeGruppen();
    List<Liste> listen = ergebnisDaten.getListen();


    String beteiligteEntitaetenString = "";
    String geloesteEntitaetString = "";
    if (customData.getKonfliktLoesung().isP2ListeEntscheidung()) {
      StringJoiner sj = new StringJoiner(" en ");
      for (Liste liste : getBeteiligteListenOderStels(customData, listen)) {
        ListeMitStimmergebnissenDocument p2ListeDokument = ergebnisDokumentPerListe.get(liste);
        sj.add(getP2ListeString(p2ListeDokument, liste));
        beteiligteP2Listen.add(p2ListeDokument);
      }
      beteiligteEntitaetenString = sj.toString();
      Liste gelosteP2Liste = customData.getGelosteListen(listen).get(0);
      gelosteListe = ergebnisDokumentPerListe.get(gelosteP2Liste);
      gelosteGruppe = GruppeDokument.EMPTY_OBJECT;
      gelosteKandidatur = CoreKandidaturDokument.EMPTY_OBJECT;
      geloesteEntitaetString = getP2ListeString(gelosteListe, gelosteP2Liste);
    } else if (customData.getKonfliktLoesung().isGruppeEntscheidung()) {
      for (PolitischeGruppe gruppe : customData.getBeteiligtePolitischeGruppen(politischeGruppen)) {
        beteiligteGruppen.add(getGruppeDokument(ergebnisDokumentPerListe, listen, gruppe));
      }
      beteiligteEntitaetenString = beteiligteGruppen.stream().map(this::getGruppeString).collect(Collectors.joining(" en "));
      gelosteGruppe = getGruppeDokument(ergebnisDokumentPerListe, listen, customData.getGelostePolitischeGruppen(politischeGruppen).get(0));
      gelosteKandidatur = CoreKandidaturDokument.EMPTY_OBJECT;
      gelosteListe = CoreWahlvorschlagsdatenDocument.EMPTY_OBJECT;
      geloesteEntitaetString = getGruppeString(gelosteGruppe);
    } else if (customData.getKonfliktLoesung().isListenKandidaturEntscheidung()) {
      for (Kandidatur kandidatur : customData.getBeteiligteKandidaturen(kandidaturen)) {
        beteiligteKandidaturen.add(ergebnisDokumentPerKandidatur.get(kandidatur));
      }
      beteiligteEntitaetenString = beteiligteKandidaturen.stream().map(this::getKandidaatString).collect(Collectors.joining(" en "));
      gelosteKandidatur = ergebnisDokumentPerKandidatur.get(customData.getGelosteKandidaturen(kandidaturen).get(0));
      gelosteGruppe = GruppeDokument.EMPTY_OBJECT;
      gelosteListe = CoreWahlvorschlagsdatenDocument.EMPTY_OBJECT;
      geloesteEntitaetString = getKandidaatString(gelosteKandidatur);
    } else {
      gelosteKandidatur = CoreKandidaturDokument.EMPTY_OBJECT;
      gelosteGruppe = GruppeDokument.EMPTY_OBJECT;
      gelosteListe = CoreWahlvorschlagsdatenDocument.EMPTY_OBJECT;
    }

    beschreibung = messages.getMessageForMessageKey("nl.dokument.p22.konflikt.beschreibung", customData.getArtikelnummer(), beteiligteEntitaetenString, geloesteEntitaetString);

    verfuegbareLosoptionen = new LinkedList<>();

    setVerfuegbareLosoptionen(customData, entityAufStimmzettelDaten, kandidaturen, politischeGruppen, listen);

    List<Liste> gelosteListen = customData.getGelosteListen(listen);
    List<PolitischeGruppe> gelostePolitischeGruppen = customData.getGelostePolitischeGruppen(politischeGruppen);
    List<Kandidatur> gelosteKandidaturen = customData.getGelosteKandidaturen(kandidaturen);

    if (!gelosteListen.isEmpty()) {
      losoptionGelost = createListeDisplayName(gelosteListen.get(0));
    } else if (!gelosteKandidaturen.isEmpty()) {
      losoptionGelost = createKandidaturDisplayName(gelosteKandidaturen.get(0), entityAufStimmzettelDaten);
    } else if (!gelostePolitischeGruppen.isEmpty()) {
      losoptionGelost = createGruppeDisplayName(gelostePolitischeGruppen.get(0));
    } else {
      losoptionGelost = "";
    }
  }

  private List<Liste> getBeteiligteListenOderStels(OsvLegacyKonfliktCustomData customData, List<Liste> listen) {
    List<Liste> beteiligteP2Listen = customData.getBeteiligteListen(listen);
    Set<Integer> geseheneStels = new HashSet<>();
    List<Liste> result = new ArrayList<>();

    for (Liste liste : beteiligteP2Listen) {
      addListeOderStelWennNichtVorhanden(geseheneStels, result, liste);
    }

    return result;
  }

  private void addListeOderStelWennNichtVorhanden(Set<Integer> geseheneStels, List<Liste> result, Liste liste) {
    Optional<Integer> stelnummer = NlListe.getStelNummerWas(liste);
    if (stelnummer.isPresent()) {
      if (!geseheneStels.contains(stelnummer.get())) {
        geseheneStels.add(stelnummer.get());
        result.add(liste);
      }
    } else {
      result.add(liste);
    }
  }

  private String getP2ListeString(CoreWahlvorschlagsdatenDocument p2ListeDokument, Liste p2Liste) {
    return NlListe.getStelNummerWas(p2Liste)
            .map(integer -> "stel " + integer)
            .orElseGet(() -> "kieskring " + p2ListeDokument.gebiet.nummer);
  }

  private String getGruppeString(GruppeDokument gruppeDokument) {
    return "politieke partij " + gruppeDokument.positionRelativ;
  }

  private String getKandidaatString(CoreKandidaturDokument kandidaturDokument) {
    return "kandidaat " + kandidaturDokument.nameAufStimmzettelFormatted;
  }

  protected boolean isKandidaturEntscheidung() {
    return !CoreKandidaturDokument.EMPTY_OBJECT.equals(gelosteKandidatur);
  }

  protected boolean isP2ListeEntscheidung() {
    return !CoreWahlvorschlagsdatenDocument.EMPTY_OBJECT.equals(gelosteListe);
  }

  protected GruppeDokument getGruppeDokument(Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe, List<Liste> listen, PolitischeGruppe gruppe) {
    return listen.stream().filter(l -> l.getGruppe().equals(gruppe)).findAny().map(key -> ergebnisDokumentPerListe.get(key).gruppe).orElse(null);
  }

  protected void setVerfuegbareLosoptionen(OsvLegacyKonfliktCustomData customData, EntityAufStimmzettelDaten entityAufStimmzettelDaten, List<Kandidatur> kandidaturen,
                                           List<PolitischeGruppe> politischeGruppen, List<Liste> listen) {
    for (Kandidatur kandidatur : customData.getBeteiligteKandidaturen(kandidaturen)) {
      verfuegbareLosoptionen.add(createKandidaturDisplayName(kandidatur, entityAufStimmzettelDaten));
    }

    for (PolitischeGruppe gruppe : customData.getBeteiligtePolitischeGruppen(politischeGruppen)) {
      verfuegbareLosoptionen.add(createGruppeDisplayName(gruppe));
    }

    for (Liste liste : customData.getBeteiligteListen(listen)) {
      verfuegbareLosoptionen.add(createListeDisplayName(liste));
    }
  }

  protected String createKandidaturDisplayName(Kandidatur kandidatur, EntityAufStimmzettelDaten entityAufStimmzettelDaten) {
    return kandidatur.getStimmzettelPersonendaten(entityAufStimmzettelDaten).getName();
  }

  protected String createGruppeDisplayName(PolitischeGruppe gruppe) {
    return gruppe.getKurzname();
  }

  protected String createListeDisplayName(Liste liste) {
    return liste.getKennwort();
  }

  public OsvLegacyArtikelnummer getOsvLegacyArtikelnummer() {
    return osvLegacyArtikelnummer;
  }
}
