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

import static java.util.Objects.nonNull;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Nonnull;

import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.gruppe.entity.GruppeTK;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.stimmeingabe.entity.GruppeStimmenAnteilComparedToVorperiodeSchwellwert;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenErfassenModel;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.jee.faces.Messages;

public class GruppeListenstimmenAnteilComparedToVorperiodeKleinerGleichSchwellwertPlausi extends AbstractStimmeingabePlausi {

  private final StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel;

  private final List<Stimmen> stimmenVorperiode;

  private final SystemGruppeEigenschaften systemGruppeEigenschaften;

  protected GruppeListenstimmenAnteilComparedToVorperiodeKleinerGleichSchwellwertPlausi(StimmeingabePlausiKonfiguration stimmeingabePlausiKonfiguration, Messages messages,
      StimmeingabeGruppeStimmenErfassenModel gruppeStimmenErfassenModel, List<Stimmen> stimmenVorperiode, SystemGruppeEigenschaften systemGruppeEigenschaften) {
    super(stimmeingabePlausiKonfiguration, messages);

    this.gruppeStimmenErfassenModel = gruppeStimmenErfassenModel;
    this.stimmenVorperiode = stimmenVorperiode;
    this.systemGruppeEigenschaften = systemGruppeEigenschaften;
  }

  @Override
  protected boolean isPlausiResultCalculatedAutomatischKorrigierbar() {
    return false;
  }

  @Override
  protected StimmeingabePlausiResult calculatePlausiResult(@Nonnull Stimmeingabe stimmeingabe) {
    GruppeStimmenAnteilComparedToVorperiodeSchwellwert schwellwert = getGruppeSchwellwert(stimmeingabe);
    if (nonNull(schwellwert)) {
      BigDecimal abweichungZurVorperiode = calculateAbweichungZurVorperiode(stimmeingabe, schwellwert);
      if (nonNull(abweichungZurVorperiode) && (nonNull(schwellwert.getUntererSchwellwert()) && abweichungZurVorperiode.compareTo(BigDecimal.valueOf(schwellwert.getUntererSchwellwert())) < 0
          || nonNull(schwellwert.getObererSchwellwert()) && abweichungZurVorperiode.compareTo(BigDecimal.valueOf(schwellwert.getObererSchwellwert())) > 0)) {
        return createPlausiResultOhneFiktiveWerteFehlerBeheber(stimmeingabe);
      }
    }
    return StimmeingabePlausiResult.KEIN_FEHLER;
  }

  GruppeStimmenAnteilComparedToVorperiodeSchwellwert getGruppeSchwellwert(@Nonnull Stimmeingabe stimmeingabe) {
    GruppeTK gruppeTK = stimmeingabe.getGruppe().getGruppeTK();
    GruppeListenstimmenAnteilComparedToVorperiodeKleinerGleichSchwellwertCustomData schwellwertCustomData = getStimmeingabePlausiKonfiguration()
        .getCustomData(GruppeListenstimmenAnteilComparedToVorperiodeKleinerGleichSchwellwertCustomData.class);
    return schwellwertCustomData.getSchwellwert(gruppeTK);
  }

  private BigDecimal calculateAbweichungZurVorperiode(@Nonnull Stimmeingabe stimmeingabe, @Nonnull GruppeStimmenAnteilComparedToVorperiodeSchwellwert schwellwert) {
    // NOTE: Viele if-Anweisungen, um unnötige Berechnungen zu vermeiden
    Long gueltigeListenstimmen = getGueltigeListenstimmen();
    if (nonNull(gueltigeListenstimmen)) {
      Long gruppeListenstimmenVorperiode = getGruppeListenstimmenVorperiode(stimmeingabe);
      if (nonNull(gruppeListenstimmenVorperiode)) {
        Long gueltigeListenstimmenVorperiode = getGueltigeListenstimmenVorperiode();
        if (nonNull(gueltigeListenstimmenVorperiode)) {
          Long gruppeListenstimmen = getGruppeListenstimmen(stimmeingabe);
          return schwellwert.getSchwellwertTyp().calculateAbweichung(gruppeListenstimmen, gueltigeListenstimmen, gruppeListenstimmenVorperiode, gueltigeListenstimmenVorperiode);
        }
      }
    }
    return null;
  }

  Long getGruppeListenstimmen(@Nonnull Stimmeingabe stimmeingabe) {
    return stimmeingabe.getAnzahl();
  }

  Long getGueltigeListenstimmen() {
    Stimmeingabe stimmeingabeGueltigeListenstimmen = gruppeStimmenErfassenModel.getStimmeingabeGruppeGueltigeListenstimmen().orElse(null);
    if (stimmeingabeGueltigeListenstimmen != null && stimmeingabeGueltigeListenstimmen.getAnzahl() > 0) {
      return stimmeingabeGueltigeListenstimmen.getAnzahl();
    }
    return null;
  }

  Long getGruppeListenstimmenVorperiode(@Nonnull Stimmeingabe stimmeingabe) {
    Stimmen gruppeListenstimmen = stimmenVorperiode.stream().filter(stimmen -> stimmen.isListenstimme() && stimmeingabe.getGruppe().getGruppeTK().equals(stimmen.getGruppe().getGruppeTK())).findFirst()
        .orElse(null);
    if (nonNull(gruppeListenstimmen)) {
      return gruppeListenstimmen.getAnzahl();
    }
    return null;
  }

  Long getGueltigeListenstimmenVorperiode() {
    Stimmen gueltigeListenstimmen = stimmenVorperiode.stream().filter(stimmen -> stimmen.isGueltigeListenstimme(systemGruppeEigenschaften)).findFirst().orElse(null);
    if (nonNull(gueltigeListenstimmen)) {
      return gueltigeListenstimmen.getAnzahl();
    }
    return null;
  }

  @Override
  protected List<Object> getStimmeingabefehlerBeschreibungParameters(Stimmeingabe stimmeingabe) {
    List<Object> result = new ArrayList<>(super.getStimmeingabefehlerBeschreibungParameters(stimmeingabe));
    GruppeStimmenAnteilComparedToVorperiodeSchwellwert gruppeSchwellwert = getGruppeSchwellwert(stimmeingabe);
    if (nonNull(gruppeSchwellwert)) {
      result.add(wertOderBeliebig(gruppeSchwellwert.getUntererSchwellwert()));
      result.add(wertOderBeliebig(gruppeSchwellwert.getObererSchwellwert()));
      result.add(gruppeSchwellwert.getSchwellwertTyp().getId());
      result.add(calculateAbweichungZurVorperiode(stimmeingabe, gruppeSchwellwert).setScale(2, RoundingMode.HALF_UP));
    }
    return result;
  }

  private Object wertOderBeliebig(Double wert) {
    if (nonNull(wert)) {
      return wert;
    }

    return getMessage("StimmeingabePlausiTyp.GRUPPE_LISTENSTIMMEN_ANTEIL_COMPARED_TO_VORPERIODE_KLEINER_GLEICH_SCHWELLWERT.fehlermeldung.null.wert");
  }
}
