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

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

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeDAO;
import de.ivu.elect.business.gruppe.boundary.PolitischeGruppeFactory;
import de.ivu.elect.business.gruppe.control.PolitischeGruppeCreate;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietDAO;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietFactory;
import de.ivu.elect.business.gruppeimgebiet.boundary.GruppeImGebietHierarchyBuilder;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietCreate;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietTK;
import de.ivu.elect.business.kandidatur.boundary.ListenkandidaturDAO;
import de.ivu.elect.business.kandidatur.boundary.ListenkandidaturFactory;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.kandidatur.entity.WaehlerBenannteKandidaturen;
import de.ivu.elect.business.person.entity.Geschlecht;
import de.ivu.elect.business.person.entity.PersonStatus;
import de.ivu.elect.business.person.entity.PersonenregisterAbgleichStatus;
import de.ivu.elect.business.person.entity.Wahlrechtsausschluss;
import de.ivu.elect.business.sitzberechnung.control.Sitzberechnungsalgorithmus;
import de.ivu.elect.business.wahl.control.Wahldaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.boundary.ListeDAO;
import de.ivu.elect.business.wahlvorschlag.boundary.ListeFactory;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.jee.faces.Messages;

public class WaehlerBenannteListeCreate {

  @Inject
  protected transient Messages messages;

  @Inject
  protected transient GruppeImGebietHierarchyBuilder gruppeImGebietHierarchyBuilder;

  @Inject
  protected transient GruppeImGebietDAO gruppeImGebietDAO;

  @Inject
  protected transient GruppeImGebietCreate gruppeImGebietCreate;

  @Inject
  protected transient GruppeImGebietFactory gruppeImGebietFactory;

  @Inject
  protected transient PolitischeGruppeDAO politischeGruppeDAO;

  @Inject
  protected transient PolitischeGruppeCreate politischeGruppeCreate;

  @Inject
  protected transient PolitischeGruppeFactory politischeGruppeFactory;

  @Inject
  protected transient ListeCreate listeCreate;

  @Inject
  protected transient ListeFactory listeFactory;

  @Inject
  protected transient ListeDAO listeDAO;

  @Inject
  protected transient ListenkandidaturDAO listenkandidaturDAO;

  @Inject
  protected transient ListenkandidaturFactory listenkandidaturFactory;

  @Inject
  private transient WaehlerBenannteListeSonstigeCreate waehlerBenannteListeSonstigeCreate;

  public WaehlerBenannteListeCreate() {
    super();
  }

  public void createWaehlerBenannteListen(Wahldaten<Wahl, Gebiet> wahldaten, Sitzberechnungsalgorithmus< ? > sitzberechnungsalgorithmus) {
    Wahl wahl = wahldaten.getWahl();

    Map<Gebiet, List<Liste>> listenNachGebiet = wahldaten.getListenNachGebiet();

    Set<Wahldurchfuehrungsgebiet> behandelteListenwahlgebiete = new HashSet<>();
    Map<GruppeImGebietTK, GruppeImGebiet> gruppenImGebietVorhanden = mapGruppeImGebietByTk(wahldaten);

    PolitischeGruppe waehlerBenannteGruppe = findWaehlerBenannteListeGruppe(wahl);

    for (Gebiet gebiet : wahldaten.getErfassungsgebiete()) {
      Wahldurchfuehrungsgebiet erfassungsgebiet = castTo(gebiet, Wahldurchfuehrungsgebiet.class);
      Wahldurchfuehrungsgebiet listenwahlgebiet = sitzberechnungsalgorithmus.getWaehlerBenannteListeListenwahlgebietFuerErfassungsgebiet(erfassungsgebiet, listenNachGebiet::get);

      if (listenwahlgebiet != null && !behandelteListenwahlgebiete.contains(listenwahlgebiet)) {
        if (waehlerBenannteGruppe == null) {
          waehlerBenannteGruppe = createWaehlerBenannteListeGruppe(wahl);
        }

        Liste waehlerBenannteListe = findWaehlerBenannteListe(listenwahlgebiet); // ELECT-9315

        if (waehlerBenannteListe == null) {
          waehlerBenannteListe = createWaehlerBenannteListe(waehlerBenannteGruppe, listenwahlgebiet, gruppenImGebietVorhanden, waehlerBenannteListeSonstigeCreate.isSonstigeAnlegen());
        }

        behandelteListenwahlgebiete.add(listenwahlgebiet);
      }
    }
  }

  @Nullable
  public Liste findWaehlerBenannteListe(Wahldurchfuehrungsgebiet listenwahlgebiet) {
    return listeDAO.findByGebiet(listenwahlgebiet).stream().filter(WaehlerBenannteKandidaturen::isWaehlerBenannteListe).findFirst().orElse(null);
  }

  public Liste createWaehlerBenannteListe(PolitischeGruppe waehlerBenannteGruppe, Wahldurchfuehrungsgebiet listenwahlgebiet, Map<GruppeImGebietTK, GruppeImGebiet> gruppenImGebietVorhanden,
      boolean listenkandidaturSonstigeCreated) {
    return createWaehlerBenannteListe(erstelleGruppeImGebiet(waehlerBenannteGruppe, listenwahlgebiet, gruppenImGebietVorhanden), listenkandidaturSonstigeCreated);
  }

  protected Liste createWaehlerBenannteListe(GruppeImGebiet gruppeImGebiet, boolean listenkandidaturSonstigeCreated) {
    gruppeImGebietDAO.flush(); // Flush, damit ListeCreate nicht bereits persistierte GruppeImGebiet-Entitäten nochmal anlegt

    Liste liste = listeFactory.create();
    liste.setKennwort(getMessage(WaehlerBenannteKandidaturen.MSG_KEY_WAEHLER_BENANNTE_LISTE_KENNWORT_DEFAULT));
    liste.setWahlvorschlagStatus(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_KANDIDATUR_WAHLVORSCHLAG_STATUS_DEFAULT);

    listeCreate.saveAndUpdateGruppeImGebiet(gruppeImGebiet, liste);

    if (listenkandidaturSonstigeCreated) {
      Listenkandidatur listenkandidatur = listenkandidaturFactory.create();
      listenkandidatur.setWahl(liste.getWahl());
      listenkandidatur.setListe(liste);
      listenkandidatur.setExterneId(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_LISTENKANDIDATUR_SONSTIGE_EXTERNE_ID);
      listenkandidatur.setListenkandidaturArt(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_LISTENKANDIDATUR_LISTENKANDIDATUR_ART_DEFAULT);
      listenkandidatur.setEingereichtePosition(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_LISTENKANDIDATUR_SONSTIGE_POSITION);
      listenkandidatur.setRelativePosition(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_LISTENKANDIDATUR_SONSTIGE_POSITION);
      listenkandidatur.setKandidaturStatus(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_KANDIDATUR_KANDIDATUR_STATUS_DEFAULT);
      listenkandidatur.setFamilienname(getMessage(WaehlerBenannteKandidaturen.MSG_KEY_WAEHLER_BENANNTE_LISTENKANDIDATUR_SONSTIGE_NAME_DEFAULT));
      listenkandidatur.setGeschlecht(Geschlecht.UNBEKANNT);
      listenkandidatur.setBemerkung(getMessage(WaehlerBenannteKandidaturen.MSG_KEY_WAEHLER_BENANNTE_LISTENKANDIDATUR_SONSTIGE_BEMERKUNG_DEFAULT));
      listenkandidatur.setPersonStatus(PersonStatus.GEEIGNET);
      listenkandidatur.setPersonenregisterAbgleichStatus(PersonenregisterAbgleichStatus.KEIN_ABGLEICH_NOTWENDIG);
      listenkandidatur.setWahlrechtsausschluss(Wahlrechtsausschluss.KEIN_AUSSCHLUSS);

      listenkandidaturDAO.persist(listenkandidatur);
    }

    return liste;
  }

  protected GruppeImGebiet erstelleGruppeImGebiet(PolitischeGruppe waehlerBenannteGruppe, Wahldurchfuehrungsgebiet listenwahlgebiet, Map<GruppeImGebietTK, GruppeImGebiet> gruppenImGebietVorhanden) {
    GruppeImGebiet gruppeImGebiet = gruppeImGebietFactory.create();

    if (waehlerBenannteGruppe != null && listenwahlgebiet != null) {
      gruppeImGebiet = gruppenImGebietVorhanden.getOrDefault(new GruppeImGebietTK(listenwahlgebiet.getGebietTK(), waehlerBenannteGruppe.getGruppeTK()), gruppeImGebiet);
    }

    gruppeImGebiet.setGebiet(listenwahlgebiet);
    gruppeImGebiet.setGruppe(waehlerBenannteGruppe);

    gruppeImGebietCreate.save(gruppeImGebiet);

    gruppenImGebietVorhanden.put(gruppeImGebiet.getGruppeImGebietTK(), gruppeImGebiet);
    gruppeImGebietHierarchyBuilder.saveAndCompleteHierarchy(gruppeImGebiet, gruppenImGebietVorhanden);

    return gruppeImGebiet;
  }

  @Nullable
  public PolitischeGruppe findWaehlerBenannteListeGruppe(Wahl wahl) {
    return politischeGruppeDAO.findByWahl(wahl).stream().filter(WaehlerBenannteKandidaturen::isWaehlerBenannteListeGruppe).findFirst().orElse(null);
  }

  public PolitischeGruppe createWaehlerBenannteListeGruppe(Wahl wahl) {
    PolitischeGruppe politischeGruppe = politischeGruppeFactory.create();

    politischeGruppe.setWahl(wahl);
    politischeGruppe.setGruppeArt(GruppeArt.PARTEI);
    politischeGruppe.setSchluessel(WaehlerBenannteKandidaturen.WAEHLER_BENANNTE_LISTENKANDIDATUR_GRUPPE_SCHLUESSEL);
    politischeGruppe.setName(getMessage(WaehlerBenannteKandidaturen.MSG_KEY_WAEHLER_BENANNTE_LISTENKANDIDATUR_GRUPPE_NAME_DEFAULT));
    politischeGruppe.setKurzname(getMessage(WaehlerBenannteKandidaturen.MSG_KEY_WAEHLER_BENANNTE_LISTENKANDIDATUR_GRUPPE_KURZNAME_DEFAULT));

    politischeGruppeCreate.save(politischeGruppe);

    return politischeGruppe;
  }

  private String getMessage(String msgKey, Object... params) {
    return messages.getMessageForMessageKey(msgKey, params);
  }

  private static Map<GruppeImGebietTK, GruppeImGebiet> mapGruppeImGebietByTk(Wahldaten<Wahl, Gebiet> wahldaten) {
    HashMap<GruppeImGebietTK, GruppeImGebiet> result = new HashMap<>();

    for (GruppeImGebiet gruppeImGebiet : wahldaten.getGruppenImGebiet()) {
      result.put(gruppeImGebiet.getGruppeImGebietTK(), gruppeImGebiet);
    }

    return result;
  }
}
