package de.ivu.elect.dokumente.ergebnis;

import de.ivu.document.generator.template.BaseDocument;
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.dokumente.wahlvorschlag.GruppeDokument;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class NlGewaehlteProGruppeDocument extends BaseDocument {

  @DocumentComplexField("Gruppe")
  public final GruppeDokument gruppe;
  @DocumentSimpleField("AnzahlSitzeDerGruppe")
  public final Long anzahlSitzeDerGruppe;
  @DocumentSimpleField("AnzahlStimmenDerGruppe")
  public final Long anzahlStimmenDerGruppe;
  @DocumentRepeat(value = "GewaehlteMitVorzugsstimmenSortiertNachStimmenzahl", elementClass = NlKandidatStimmenDocument.class, itemName = "gewaehlt")
  public final List<NlKandidatStimmenDocument> gewaehlteMitVorzugsstimmenSortiertNachStimmenzahl;
  @DocumentRepeat(value = "GewaehlteNichtMitVorzugsstimmenSortiertNachRang", elementClass = NlKandidatStimmenDocument.class, itemName = "gewaehlt")
  public final List<NlKandidatStimmenDocument> gewaehlteNichtMitVorzugsstimmenSortiertNachRang;
  @DocumentRepeat(value = "KandidatenVonListenMitGeaenderterReihenfolge", elementClass = NlKandidatenStimmenProListeDocument.class, itemName = "liste")
  public final List<NlKandidatenStimmenProListeDocument> listenMitGeaenderterReihenfolge;

  public NlGewaehlteProGruppeDocument(Long anzahlSitzeDerGruppe,
                                      List<NlKandidatenStimmenProListeDocument> kandidatenStimmenDokumente) {

    if (!kandidatenStimmenDokumente.isEmpty()) {
      this.gruppe = kandidatenStimmenDokumente.get(0).gruppe;
    } else {
      this.gruppe = GruppeDokument.EMPTY_OBJECT;
    }
    anzahlStimmenDerGruppe = kandidatenStimmenDokumente.stream()
            .map(d -> d.anzahlStimmenDerListe)
            .reduce(Long::sum).orElse(0L);
    this.anzahlSitzeDerGruppe = anzahlSitzeDerGruppe;
    gewaehlteMitVorzugsstimmenSortiertNachStimmenzahl = getUeberVorzugGewaehltenDokumenteSortiertNachGesamterStimmenzahl(kandidatenStimmenDokumente);

    gewaehlteNichtMitVorzugsstimmenSortiertNachRang = kandidatenStimmenDokumente.stream()
            .flatMap(ks -> ks.kandidatenStimmenzahl.stream()
                    .filter(k -> !k.isUeberVorzugGewaehlt))
            .sorted(Comparator.comparingLong(k -> k.rang))
            .collect(Collectors.toList());

    listenMitGeaenderterReihenfolge = kandidatenStimmenDokumente.stream()
            .filter(k -> k.isMitGeaenderterReihenfolge)
            .collect(Collectors.toList());

  }

  private List<NlKandidatStimmenDocument> getUeberVorzugGewaehltenDokumenteSortiertNachGesamterStimmenzahl(List<NlKandidatenStimmenProListeDocument> kandidatenStimmenDokumente) {
    //ELECT-10767
    List<NlKandidatStimmenDocument> alleKandidaturen = kandidatenStimmenDokumente.stream()
            .flatMap(ks -> ks.kandidatenStimmenzahl.stream())
            .collect(Collectors.toList());

    List<NlKandidatStimmenDocument> mitVorzuggstimmenKandidaturen = alleKandidaturen.stream()
            .filter(ks -> ks.isUeberVorzugGewaehlt)
            .collect(Collectors.toList());

    Map<NlKandidatStimmenDocument, Long> gewaehlteMitVorzugsstimmen = new HashMap<>();
    for (NlKandidatStimmenDocument nlKandidatStimmenDocument : mitVorzuggstimmenKandidaturen) {
      gewaehlteMitVorzugsstimmen.put(nlKandidatStimmenDocument, getGesammteStimmenDesKandidaten(alleKandidaturen, nlKandidatStimmenDocument));
    }

    return sortDokumenteNachGesamterStimmenzahl(gewaehlteMitVorzugsstimmen);
  }

  private List<NlKandidatStimmenDocument> sortDokumenteNachGesamterStimmenzahl(Map<NlKandidatStimmenDocument, Long> gewaehlteMitVorzugsstimmen) {
    //ELECT-11312
    return gewaehlteMitVorzugsstimmen.keySet().stream()
            .sorted((u, v) -> Long.compare(gewaehlteMitVorzugsstimmen.get(v), gewaehlteMitVorzugsstimmen.get(u)))
            .collect(Collectors.toList());
  }

  private Long getGesammteStimmenDesKandidaten(List<NlKandidatStimmenDocument> alleKandidaturen, NlKandidatStimmenDocument nlKandidatStimmenDocument) {
    return alleKandidaturen.stream()
            .filter(k -> k.getListenkandidatur().equals(nlKandidatStimmenDocument.getListenkandidatur()) || k.getListenkandidatur().getIdentischVerknuepfteKandidaturen().stream().anyMatch(ik -> nlKandidatStimmenDocument.getListenkandidatur().equals(ik)))
            .map(k -> k.anzahlStimmen)
            .reduce(Long::sum).orElse(0L);
  }
}
