
package de.ivu.elect.business.stimmeingabe.control.fiktive;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.hochrechnung.entity.AnzahlStimmenProWaehlerNachStimmenartBestimmer;
import de.ivu.elect.business.hochrechnung.entity.GebietDaten;
import de.ivu.elect.business.hochrechnung.entity.ParteiStimmen;
import de.ivu.elect.business.hochrechnung.entity.StimmenhochrechnerGebietAusgabedaten;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeListenkandidaturStimmen;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeListenkandidaturStimmenErfassenModel;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;


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

public class GruppenStimmeingabenAusFiktivwertenSetzer extends AbstractStimmeingabenAusFiktivwertenSetzer {

  public GruppenStimmeingabenAusFiktivwertenSetzer() {
    super();
  }

  @Override
  public boolean isVomLetztenErgebniseingangUebernommeneStimmenZuUeberschreiben(Ergebniseingang ergebniseingangAktuell, Collection<Stimmeingabe> uebernommeneStimmeingaben) {
    if (ergebniseingangAktuell.isErgebniseingangsartVorlaeufig()) {
      return true; // Vorlaeufiger Ergebniseingang kann ueberschrieben werden
    }

    // ueberschreibe, wenn es dann keine echt positive Zahlen vorhanden sind.
    return uebernommeneStimmeingaben.stream().noneMatch(stimmeingabe -> stimmeingabe != null && stimmeingabe.hasAnzahlGroesserNull());
  }

  @Override
  public void setzeErgebniseingangStimmeingabenAnzahlAusHochgerechnetenWerten(Ergebniseingang ergebniseingang, StimmenhochrechnerGebietAusgabedaten hochrechnerAusgabedaten, AnzahlStimmenProWaehlerNachStimmenartBestimmer anzahlStimmenProWaehlerBestimmer) {
    StimmeingabeGruppeStimmenErfassenModel stimmeingabeGruppeStimmenErfassenModel = ergebniseingang.getStimmeingabeGruppeStimmenErfassenModel();
    if (stimmeingabeGruppeStimmenErfassenModel == null || hochrechnerAusgabedaten == null) {
      return;
    }

    GebietDaten<Long> hochgerechnetAlsAnzahlen = hochrechnerAusgabedaten.getHochgerechnetAlsAnzahlen(anzahlStimmenProWaehlerBestimmer);
    if (hochgerechnetAlsAnzahlen == null) {
      return;
    }

    if (stimmeingabeGruppeStimmenErfassenModel.hasStimmenGruppeUngueltige()) {
      setUngueltigeStimmeingabeAusHochgerechnetenWertenWennUeberschreibbar(ergebniseingang, stimmeingabeGruppeStimmenErfassenModel, hochgerechnetAlsAnzahlen, anzahlStimmenProWaehlerBestimmer);
    }

    Collection<Stimmeingabe> stimmeingabenGruppenstimmen = stimmeingabeGruppeStimmenErfassenModel.getStimmeingabenPolitischeGruppen();

    if (isStimmeingabenPolitischeGruppeListenstimmenErfasstAufListenkandidaturStimmenUnterseite(stimmeingabenGruppenstimmen, ergebniseingang.getWahldurchfuehrung())) {
      Collection<StimmeingabeListenkandidaturStimmenErfassenModel> listenkandidaturStimmenErfassenModels = ergebniseingang
          .getStimmeingabeListenkandidaturStimmenErfassenModels();
      stimmeingabenGruppenstimmen = collectStimmeingabenPolitischeGruppeListenstimmenErfasstAufListenkandidaturUnterseite(listenkandidaturStimmenErfassenModels);
    }

    if (isVomLetztenErgebniseingangUebernommeneStimmenZuUeberschreiben(ergebniseingang, stimmeingabenGruppenstimmen)) {
      stimmeingabenGruppenstimmen
          .forEach(stimmeingabenGruppeStimmen -> setzeGruppenstimmenAnzahlAusHochgerechnetenWerten(stimmeingabenGruppeStimmen, hochgerechnetAlsAnzahlen));
    }
  }
  
  // NOTE, dass bspw. bei der DD SRW/ORW die Listenstimmen der Parteien nicht auf der erste (Gruppenstimmen-) Erfassungsmaske erfasst werden, sondern erst auf der Erfassungsmaske für Listenkandidaturen!!!
  private List<Stimmeingabe> collectStimmeingabenPolitischeGruppeListenstimmenErfasstAufListenkandidaturUnterseite(
      Collection<StimmeingabeListenkandidaturStimmenErfassenModel> listenkandidaturStimmenErfassenModels) {
    return listenkandidaturStimmenErfassenModels.stream() //
        .filter(StimmeingabeListenkandidaturStimmenErfassenModel::hasStimmeingabeListenkandidaturStimmenGesamt) //
        .map(StimmeingabeListenkandidaturStimmenErfassenModel::getListenkandidaturStimmenGesamt) //
        .filter(stimmeingabeListenkandidaturStimmen -> stimmeingabeListenkandidaturStimmen.getStimmeingaben().size() == 1) //
        .map(StimmeingabeListenkandidaturStimmen::getStimmeingabe) //
        .collect(toList());
  }

  private void setUngueltigeStimmeingabeAusHochgerechnetenWertenWennUeberschreibbar(Ergebniseingang ergebniseingangAktuell, StimmeingabeGruppeStimmenErfassenModel stimmeingabeGruppeStimmenErfassenModel, GebietDaten<Long> hochgerechnetAlsAnzahlen, AnzahlStimmenProWaehlerNachStimmenartBestimmer anzahlStimmenProWaehlerBestimmer) {
    Optional<Stimmeingabe> stimmeingabeGruppeUngueltigeDirektstimmen = stimmeingabeGruppeStimmenErfassenModel.getStimmeingabeGruppeUngueltigeDirektstimmen();
    Optional<Stimmeingabe> stimmeingabeGruppeUngueltigeListenstimmen = stimmeingabeGruppeStimmenErfassenModel.getStimmeingabeGruppeUngueltigeListenstimmen();
    List<Stimmeingabe> stimmeingabenGruppeUngueltige = new ArrayList<>();
    stimmeingabeGruppeUngueltigeDirektstimmen.ifPresent(stimmeingabenGruppeUngueltige::add);
    stimmeingabeGruppeUngueltigeListenstimmen.ifPresent(stimmeingabenGruppeUngueltige::add);


    if (!isVomLetztenErgebniseingangUebernommeneStimmenZuUeberschreiben(ergebniseingangAktuell, stimmeingabenGruppeUngueltige)) {
      return;
    }

    Long ungueltigeDirekt = getAnzahlUngueltigeStimmzettel(Stimmenart.DIREKTSTIMME, hochgerechnetAlsAnzahlen, anzahlStimmenProWaehlerBestimmer.getAnzahlVomWaehlerBenutzteStimmenDurchschnittlich(Stimmenart.DIREKTSTIMME, hochgerechnetAlsAnzahlen.getGebiet()));
    Long ungueltigeListen = getAnzahlUngueltigeStimmzettel(Stimmenart.LISTENSTIMME, hochgerechnetAlsAnzahlen, anzahlStimmenProWaehlerBestimmer.getAnzahlVomWaehlerBenutzteStimmenDurchschnittlich(Stimmenart.LISTENSTIMME, hochgerechnetAlsAnzahlen.getGebiet()));

    if (ungueltigeDirekt != null) {
      setzeStimmenEingabeAnzahl(ungueltigeDirekt, stimmeingabeGruppeUngueltigeDirektstimmen);
    }
    if (ungueltigeListen != null) {
      setzeStimmenEingabeAnzahl(ungueltigeListen, stimmeingabeGruppeUngueltigeListenstimmen);
    }
  }

  protected Long getAnzahlUngueltigeStimmzettel(Stimmenart stimmenart, GebietDaten<Long> hochgerechnetAlsAnzahlen, Long vomWaehlerImDurchschnittVergebeneStimmenAnzahl) {
    Long anzahlStimmen = hochgerechnetAlsAnzahlen.getUngueltige().get(stimmenart);
    if (anzahlStimmen != null) {
      return anzahlStimmen / vomWaehlerImDurchschnittVergebeneStimmenAnzahl;
    }
    return null;
  }

  private void setzeGruppenstimmenAnzahlAusHochgerechnetenWerten(Stimmeingabe stimmeingabenGruppeStimmen, GebietDaten<Long> hochgerechnetAlsAnzahlen) {
    PolitischeGruppe politischeGruppe = stimmeingabenGruppeStimmen.getPolitischeGruppe();
    Stimmenart stimmenart = stimmeingabenGruppeStimmen.getStimmenart();
    if (politischeGruppe == null || stimmenart == null) {
      return;
    }
    ParteiStimmen<Long> parteiStimmen = hochgerechnetAlsAnzahlen.getParteiStimmen().get(politischeGruppe);
    if (parteiStimmen != null) {
      setzeStimmenEingabeAnzahl(parteiStimmen.getStimmen(stimmenart), stimmeingabenGruppeStimmen);
    }
  }



  private void assertModelleVorhanden(Ergebniseingang ergebniseingang) {
    if (!ergebniseingang.isStimmeingabeKopfzahlErfassenModelSet() || !ergebniseingang.isStimmeingabeGruppeStimmenErfassenModelSet()) {
      throw new IllegalArgumentException("Dem Ergebniseingang [" + ergebniseingang + "] fehlt zum Ermitteln fiktiver " + "Werte mindestens ein Modell. [stimmeingabeKopfzahlErfassenModel = "
          + ergebniseingang.getStimmeingabeKopfzahlErfassenModel() + ", gruppeStimmenModel = " + ergebniseingang.getStimmeingabeGruppeStimmenErfassenModel() + "]");
    }
  }

  protected boolean isStimmeingabenPolitischeGruppeListenstimmenErfasstAufListenkandidaturStimmenUnterseite(Collection<Stimmeingabe> stimmeingabenGruppenstimmen, Wahldurchfuehrung wahldurchfuehrung) {
    return stimmeingabenGruppenstimmen.isEmpty();
  };
}
