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

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.business.ergebnis.control.NlErgebnisDaten;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.GruppePK;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagPK;
import de.ivu.elect.dokumente.ergebnis.adapter.Fraction;
import de.ivu.elect.dokumente.ergebnis.adapter.NlErgebnisDaten2ErgebnisDokumentAdapter;
import de.ivu.elect.dokumente.wahlvorschlag.GruppeDokument;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static java.util.stream.Collectors.toList;

public class GruppeUnterverteilungenDokument extends BaseDocument {

  @DocumentComplexField("Gruppe")
  public final GruppeDokument gruppe;
  @DocumentSimpleField("AnzahlSitze")
  public final Long anzahlSitze;
  @DocumentSimpleField("AnzahlStimmen")
  public final Long anzahlStimmen;
  @DocumentSimpleField("WahlteilerBeiUnterverteilungInGruppe") //groepskiesdeler
  public final String wahlteilerBeiUnterverteilungInGruppe;
  @DocumentRepeat(value = "UnterverteilungZeilen", elementClass = AbstractUnterverteilungZeile.class, itemName = "zeile")
  public final List<AbstractUnterverteilungZeile> zeilen;


  public GruppeUnterverteilungenDokument(NlErgebnisDaten2ErgebnisDokumentAdapter adapter, NlErgebnisDaten ergebnisDaten,
                                         GruppeDokument gruppeDokument, GruppePK gruppePK,
                                         Map<Integer, List<Liste>> listenSortiertNachGebietsnummerProStelMap) {
    this.gruppe = gruppeDokument;

    anzahlSitze = adapter.getAnzahlSitze(gruppePK).orElse(0);
    anzahlStimmen = adapter.getAnzahlStimmen(gruppePK).orElse(0);
    wahlteilerBeiUnterverteilungInGruppe = adapter.getWahlteilerBeiUnterverteilungInGruppe(gruppePK)
            .map(Fraction::toString)
            .orElse(null);

    if (wahlteilerBeiUnterverteilungInGruppe != null) {
      zeilen = erzeugeZeilen(listenSortiertNachGebietsnummerProStelMap, gruppePK, adapter, ergebnisDaten);
    } else {
      zeilen = Collections.emptyList();
    }
  }

  private static List<AbstractUnterverteilungZeile> erzeugeZeilen(Map<Integer, List<Liste>> listenSortiertNachGebietsnummerProStelMap,
                                                                  GruppePK gruppePk, NlErgebnisDaten2ErgebnisDokumentAdapter adapter,
                                                                  NlErgebnisDaten ergebnisDaten) {
    List<AbstractUnterverteilungZeile> result = new ArrayList<>();
    List<AbstractUnterverteilungZeile> stelloseListenZeilen = new ArrayList<>();

    for (Entry<Integer, List<Liste>> stelListEntry : listenSortiertNachGebietsnummerProStelMap.entrySet()) {
      Integer stelnummer = stelListEntry.getKey();
      List<Liste> listenNachGebietsnummer = stelListEntry.getValue();

      if (isListenEinesStels(stelnummer)) {
        result.addAll(createUnterverteilungZeilenFuerStel(stelnummer, listenNachGebietsnummer, gruppePk, ergebnisDaten, adapter));
      } else {
        stelloseListenZeilen.addAll(createStelloseListenZeilen(listenNachGebietsnummer, gruppePk, adapter, ergebnisDaten));
      }
    }

    //Diese Zeilen stehen in der Tabelle an letzer Stelle.
    result.addAll(stelloseListenZeilen);
    return result;
  }

  private static List<AbstractUnterverteilungZeile> createStelloseListenZeilen(List<Liste> listenNachGebietsnummer,
                                                                               GruppePK gruppePk, NlErgebnisDaten2ErgebnisDokumentAdapter adapter,
                                                                               NlErgebnisDaten ergebnisDaten) {
    return listenNachGebietsnummer.stream()
            .map(liste -> createWahlbezirkUnterverteilungZeile(gruppePk, liste, ergebnisDaten, adapter))
            .collect(toList());
  }

  private static List<AbstractUnterverteilungZeile> createUnterverteilungZeilenFuerStel(Integer stelnummer,
                                                                                        List<Liste> listenDesStelsNachGebietsnummer,
                                                                                        GruppePK gruppePk, NlErgebnisDaten ergebnisDaten,
                                                                                        NlErgebnisDaten2ErgebnisDokumentAdapter adapter) {
    List<AbstractUnterverteilungZeile> result = new ArrayList<>();

    long stimmenAnzahlDesStels = 0;
    for (Liste liste : listenDesStelsNachGebietsnummer) {
      Wahldurchfuehrungsgebiet gebiet = liste.getGebiet().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
      long stimmenDerListe = ergebnisDaten.getSummeListeListenstimmen(gebiet, Stimmabgabeart.GESAMT, liste);
      stimmenAnzahlDesStels += stimmenDerListe;
      result.add(new WahlbezirkUnterverteilungZeile(gebiet.getNummer(), stimmenDerListe));
    }

    String stelId = NlListe.createStelKey(gruppePk, stelnummer);
    result.add(createStelUnterverteilungZeile(stelnummer, gruppePk, stelId, stimmenAnzahlDesStels, adapter));
    result.add(new LeereUnterverteilungZeile());
    return result;
  }

  private static StelUnterverteilungZeile createStelUnterverteilungZeile(Integer stelnummer, GruppePK gruppePk, String stelId,
                                                                  long stimmenAnzahlDesStels, NlErgebnisDaten2ErgebnisDokumentAdapter adapter) {
    return new StelUnterverteilungZeile(stelnummer, stimmenAnzahlDesStels,
            adapter.getAnzahlSitzeAusErsterZuteilungFuerStelInGruppe(stelId, gruppePk),
            adapter.getRestNachErsterZuteilungFuerStelInGruppe(stelId, gruppePk),
            adapter.getAnzahlSitzeAusNiemeyerVerteilungFuerStelInGruppe(stelId, gruppePk),
            adapter.getGesamtzahlSitzeNachNiemeyerVerteilungFuerStelInGruppe(stelId, gruppePk));
  }

  private static WahlbezirkUnterverteilungZeile createWahlbezirkUnterverteilungZeile(GruppePK gruppePk, Liste liste, NlErgebnisDaten ergebnisDaten,
                                                                                     NlErgebnisDaten2ErgebnisDokumentAdapter adapter) {
    Wahldurchfuehrungsgebiet gebiet = liste.getGebiet().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
    WahlvorschlagPK listePk = liste.getPk();

    return new WahlbezirkUnterverteilungZeile(gebiet.getNummer(), ergebnisDaten.getSummeListeListenstimmen(gebiet, Stimmabgabeart.GESAMT, liste),
            adapter.getAnzahlSitzeAusErsterZuteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getRestNachErsterZuteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getAnzahlSitzeAusNiemeyerVerteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getGesamtzahlSitzeNachNiemeyerVerteilungFuerListeInGruppe(listePk, gruppePk));
  }

  private static boolean isListenEinesStels(Integer stelnummer) {
    return stelnummer != P22ErgebnisDatenCustomDataDocument.LISTE_OHNE_STEL_KEY;
  }
}
