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

import static de.ivu.elect.business.gebietsbaum.control.EntityMitWurzelgebietDAO.fromDao;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import de.ivu.elect.business.exchange.control.AbstractWahlExchangeContext;
import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.KandidaturExtendedTK;
import de.ivu.elect.business.wahl.entity.Wahl;

public class WahlvorschlagImportContext extends AbstractWahlExchangeContext {

  private static final long serialVersionUID = 1L;

  // Default aus WahlvorschlagImporter übernommen
  public static final Function<Collection<Kandidatur>, Map<KandidaturExtendedTK, Kandidatur>> DEFAULT_KANDIDATUREN_NACH_TK_FUNCTION = kandidaturen -> {
    Map<KandidaturExtendedTK, Kandidatur> result = new HashMap<>(kandidaturen.size() * 2);

    for (Kandidatur entity : kandidaturen) {
      result.put(new KandidaturExtendedTK(entity, KandidaturExtendedTK.LISTENKANDIDATUR_VORGEGEBENE_POSITION), entity);
    }

    return result;
  };

  private static final WahlvorschlagImporterZulassungsVerhalten DEFAULT_ZULASSUNGS_VERHALTEN = WahlvorschlagImporterZulassungsVerhalten.ZULASSUNGSSTATUS_IMPORTIEREN; // ELECT-12623

  private final Gebiet wurzelgebiet;

  private final List<Gebiet> listenwahlgebiete;

  private final List<Gebiet> direktwahlgebiete;

  private WahlvorschlagImporterZulassungsVerhalten zulassungsVerhalten;

  private Boolean kennwortVonGruppeUebernehmenWennNichtGesetzt;

  // ELECT-4324 ELECT-5265
  private Boolean kurznameVonGruppeUebernehmenWennNichtGesetzt;
  private final List<Gebiet> berechtigeGebiete;
  private Map<GebietTK, Gebiet> berechtigteGebieteTKGebietMap = null;
  private boolean isUntergeordneteGebieteImportieren = false;
  private Gebiet selectedGebiet;

  private boolean isUnvollstaendigeDatensaetzeImportieren = false;

  private boolean isKreiswahlleiter = false;

  private Function<Collection<Kandidatur>, Map<KandidaturExtendedTK, Kandidatur>> kandidaturenNachTkFunction;

  public WahlvorschlagImportContext(Wahl wahl, @Nonnull AbstractGebietDAO gebietDAO) {
    this(wahl, gebietDAO, DEFAULT_ZULASSUNGS_VERHALTEN);
  }

  public WahlvorschlagImportContext(Wahl wahl, @Nonnull AbstractGebietDAO gebietDAO, WahlvorschlagImporterZulassungsVerhalten zulassungsVerhalten) {
    this(wahl, findWurzelgebiet(wahl, gebietDAO), findListenwahlgebiete(wahl, gebietDAO), findDirektwahlgebiete(wahl, gebietDAO), zulassungsVerhalten, Collections.emptyList());
  }

  public WahlvorschlagImportContext(Wahl wahl, List<Gebiet> berechtigeGebiete, @Nonnull AbstractGebietDAO gebietDAO) {
    this(wahl, findWurzelgebiet(wahl, gebietDAO), findListenwahlgebiete(wahl, gebietDAO), findDirektwahlgebiete(wahl, gebietDAO), DEFAULT_ZULASSUNGS_VERHALTEN, berechtigeGebiete);
  }

  public WahlvorschlagImportContext(Wahl wahl, @Nullable Gebiet wurzelgebiet, List<Gebiet> listenwahlgebiete, List<Gebiet> direktwahlgebiete,
      WahlvorschlagImporterZulassungsVerhalten zulassungsVerhalten, List<Gebiet> berechtigeGebiete) {
    super(wahl);

    this.wurzelgebiet = wurzelgebiet;
    this.listenwahlgebiete = listenwahlgebiete;
    this.direktwahlgebiete = direktwahlgebiete;

    this.zulassungsVerhalten = zulassungsVerhalten;

    // Standardwerte NICHT ÄNDERN -> sind nur gezielt beim manuellen Import überschreibbar!
    this.kennwortVonGruppeUebernehmenWennNichtGesetzt = true;
    this.kurznameVonGruppeUebernehmenWennNichtGesetzt = true;

    this.berechtigeGebiete = unmodifiableList(berechtigeGebiete);
  }

  public Gebiet getWurzelgebiet() {
    return wurzelgebiet;
  }

  public List<Gebiet> getListenwahlgebiete() {
    return listenwahlgebiete;
  }

  public List<Gebiet> getDirektwahlgebiete() {
    return direktwahlgebiete;
  }

  public WahlvorschlagImporterZulassungsVerhalten getZulassungsVerhalten() {
    return zulassungsVerhalten;
  }

  public void setZulassungsVerhalten(WahlvorschlagImporterZulassungsVerhalten zulassungsVerhalten) {
    this.zulassungsVerhalten = zulassungsVerhalten;
  }

  public Boolean getKurznameVonGruppeUebernehmenWennNichtGesetzt() {
    return Boolean.TRUE.equals(kurznameVonGruppeUebernehmenWennNichtGesetzt);
  }

  public void setKurznameVonGruppeUebernehmenWennNichtGesetzt(boolean kurznameVonGruppeUebernehmenWennNichtGesetzt) {
    this.kurznameVonGruppeUebernehmenWennNichtGesetzt = kurznameVonGruppeUebernehmenWennNichtGesetzt;
  }

  public Boolean getKennwortVonGruppeUebernehmenWennNichtGesetzt() {
    return Boolean.TRUE.equals(kennwortVonGruppeUebernehmenWennNichtGesetzt);
  }

  public void setKennwortVonGruppeUebernehmenWennNichtGesetzt(boolean kennwortVonGruppeUebernehmenWennNichtGesetzt) {
    this.kennwortVonGruppeUebernehmenWennNichtGesetzt = kennwortVonGruppeUebernehmenWennNichtGesetzt;
  }

  private static Gebiet findWurzelgebiet(Wahl wahl, AbstractGebietDAO gebietDAO) {
    if (wahl == null) return null;
    Gebiet wurzelgebiet = wahl.getWurzelgebiet(fromDao(gebietDAO));
    if (wurzelgebiet != null) {
      wurzelgebiet.getGebietTK().getGebietsartTK(); // eager loading
    }
    return wurzelgebiet;
  }

  private static List<Gebiet> findListenwahlgebiete(Wahl wahl, AbstractGebietDAO gebietDAO) {
    if (wahl == null) {
      return emptyList();
    }
    List<Gebiet> listengebiete = gebietDAO.findByWahlAndListengebiet(wahl, true);
    listengebiete.forEach(gebiet -> gebiet.getGebietTK().getGebietsartTK()); // eager loading
    return listengebiete;
  }

  private static List<Gebiet> findDirektwahlgebiete(Wahl wahl, AbstractGebietDAO gebietDAO) {
    if (wahl == null) {
      return emptyList();
    }
    List<Gebiet> direktwahlgebiete = gebietDAO.findByWahlAndDirektwahlgebiet(wahl, true);
    direktwahlgebiete.forEach(gebiet -> gebiet.getGebietTK().getGebietsartTK()); // eager loading
    return direktwahlgebiete;
  }

  public List<Gebiet> getBerechtigeGebiete() {
    return berechtigeGebiete;
  }

  public Map<GebietTK, Gebiet> getBerechtigteGebieteTKGebietMap() {
    if (berechtigteGebieteTKGebietMap == null) {
      berechtigteGebieteTKGebietMap = GebietTK.mappeGebietNachTK(getBerechtigeGebiete());
    }

    return berechtigteGebieteTKGebietMap;
  }

  public void setUntergeordneteGebieteImportieren(boolean isUntergeordneteGebieteImportieren) {
    this.isUntergeordneteGebieteImportieren = isUntergeordneteGebieteImportieren;
  }

  public boolean isUntergeordneteGebieteImportieren() {
    return isUntergeordneteGebieteImportieren;
  }

  public void setSelectedGebiet(Gebiet selectedGebiet) {
    this.selectedGebiet = selectedGebiet;
  }

  public Gebiet getSelectedGebiet() {
    return selectedGebiet;
  }

  public boolean isUnvollstaendigeDatensaetzeImportieren() {
    return isUnvollstaendigeDatensaetzeImportieren;
  }

  public void setUnvollstaendigeDatensaetzeImportieren(boolean unvollstaendigeDatensaetzeImportieren) {
    isUnvollstaendigeDatensaetzeImportieren = unvollstaendigeDatensaetzeImportieren;
  }

  public void setKreiswahlleiter(boolean isKreiswahlleiter) {
    this.isKreiswahlleiter = isKreiswahlleiter;
  }

  public boolean isKreiswahlleiter() {
    return isKreiswahlleiter;
  }

  public Function<Collection<Kandidatur>, Map<KandidaturExtendedTK, Kandidatur>> getKandidaturenNachTkFunction() {
    if (kandidaturenNachTkFunction == null) {
      kandidaturenNachTkFunction = DEFAULT_KANDIDATUREN_NACH_TK_FUNCTION;
    }

    return kandidaturenNachTkFunction;
  }

  public void setKandidaturenNachTkFunction(Function<Collection<Kandidatur>, Map<KandidaturExtendedTK, Kandidatur>> kandidaturenNachTkFunction) {
    this.kandidaturenNachTkFunction = kandidaturenNachTkFunction;
  }

  public KandidaturExtendedTK getKandidaturExtendedTK(Kandidatur kandidatur) {
    return getKandidaturenNachTkFunction().apply(Collections.singleton(kandidatur)).keySet().iterator().next();
  }
}
