
package de.ivu.elect.business.kandidatur.boundary;

import java.io.Serializable;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.kandidatur.entity.WaehlerBenannteKandidaturen;
import de.ivu.elect.business.sitzberechnung.control.ListenwahlMehrheitswahlPruefer;
import de.ivu.elect.business.sitzberechnung.control.ListenwahlMehrheitswahlPrueferFactory;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;


import static de.ivu.elect.jee.entity.EntityUtil.castTo;

public class WaehlerBenannteKandidaturKonfiguration implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient ListenwahlMehrheitswahlPrueferFactory listenwahlMehrheitswahlPrueferFactory;

  public WaehlerBenannteKandidaturKonfiguration() {
    super();
  }

  public boolean isWaehlerBenannteDirektkandidaturManuellAnlegenZulaessig(Gebiet gebiet, Function<Gebiet, Collection<Direktkandidat>> wahlvorschlaegeInDirektwahlgebietFct) {
    if (gebiet != null && gebiet.isDirektwahlgebiet()) {
      return isWaehlerBenannteWahlvorschlaegeManuellErfassbar(gebiet, () -> wahlvorschlaegeInDirektwahlgebietFct.apply(gebiet));
    }

    return false;
  }

  public boolean isWaehlerBenannteListeManuellAnlegenZulaessig(Gebiet gebiet, Function<Gebiet, Collection<Liste>> wahlvorschlaegeInListenwahlgebietFct) {
    if (gebiet != null && gebiet.isListenwahlgebiet()) {
      Collection<Liste> wahlvorschlaege = wahlvorschlaegeInListenwahlgebietFct.apply(gebiet);

      return !containsWaehlerBenannteListe(wahlvorschlaege) && isWaehlerBenannteWahlvorschlaegeManuellErfassbar(gebiet, () -> wahlvorschlaege);
    }

    return false;
  }

  protected boolean containsWaehlerBenannteListe(Collection<Liste> listen) {
    return listen.stream().anyMatch(WaehlerBenannteKandidaturen::isWaehlerBenannteListe);
  }

  public boolean isWaehlerBenannteWahlvorschlaegeManuellErfassbar(Gebiet gebiet, Supplier<Collection<? extends Wahlvorschlag>> wahlvorschlaegeFct) {
    if (gebiet != null) {
      Wahl wahl = gebiet.getWahl();

      if (wahl != null) {

        return bestimmeWaehlerbenanntErfassbarFuerGebiet(gebiet, wahlvorschlaegeFct, wahl);

        // TODO ELECT-16341 return (boolean) gebiet.getTransientContext().computeIfAbsent("WaehlerBenannteKandidaturKonfiguration" +
        //   ".isWaehlerBenannteWahlvorschlaegeManuellErfassbar", x -> bestimmeWaehlerbenanntErfassbarFuerGebiet(gebiet, wahlvorschlaegeFct, wahl));
      }
    }

    return false;
  }

  private boolean bestimmeWaehlerbenanntErfassbarFuerGebiet(Gebiet gebiet, Supplier<Collection<? extends Wahlvorschlag>> wahlvorschlaegeFct, Wahl wahl) {
    if (wahl.isWahlMitDirektkandidatengebieten()) {
      return !wahl.isWahlMitListengebieten() && countDirektkandidatenAufStimmzettelWaehlerBenannteDirektkandidatenExcluded(
          wahlvorschlaegeFct.get()) <= 1;
      // ELECT-9317
    }

    return getListenwahlMehrheitswahlPruefer(wahl).isMehrheitswahl(gebiet, wahlvorschlaegeFct);
  }

  private long countDirektkandidatenAufStimmzettelWaehlerBenannteDirektkandidatenExcluded(Collection<? extends Wahlvorschlag> wahlvorschlaege) {
    int result = 0;

    for (Wahlvorschlag wahlvorschlag : wahlvorschlaege) {
      if (wahlvorschlag.isDirektkandidat() && wahlvorschlag.isAufStimmzettel() && !WaehlerBenannteKandidaturen.isWaehlerBenannterDirektkandidat(
          castTo(wahlvorschlag, Direktkandidat.class))) {
        result++;
      }
    }

    return result;
  }

  protected ListenwahlMehrheitswahlPruefer getListenwahlMehrheitswahlPruefer(Wahl wahl) {
    return listenwahlMehrheitswahlPrueferFactory.createMehrheitswahlPruefer(wahl);
  }
}
