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

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.ejb.Stateless;
import javax.inject.Inject;

import de.ivu.elect.business.gruppe.control.PolitischeGruppeCreate;
import de.ivu.elect.business.gruppe.entity.GruppeUebernehmenParameter;
import de.ivu.elect.business.gruppe.entity.GruppeUebernehmenResult;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppeTK;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.entity.Wahl;


import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;

@Stateless
public class GruppeUebernehmen {

  @Inject
  private transient WahlDAO wahlDAO;

  @Inject
  private transient PolitischeGruppeDAO politischeGruppeDAO;

  @Inject
  private transient PolitischeGruppeFactory politischeGruppeFactory;

  @Inject
  private transient PolitischeGruppeCreate politischeGruppeCreate;

  @Inject
  private transient GruppeEdit gruppeEdit;

  public GruppeUebernehmen() {
    super();
  }

  public GruppeUebernehmenResult save(GruppeUebernehmenParameter gruppeUebernehmenParameter) {
    if (gruppeUebernehmenParameter.getWahlQuelle() == null || gruppeUebernehmenParameter.getWahlZiel() == null) {
      return GruppeUebernehmenResult.NICHTS_UEBRNOMMEN;
    }

    var quelle = wahlDAO.attach(gruppeUebernehmenParameter.getWahlQuelle());
    var ziel = wahlDAO.attach(gruppeUebernehmenParameter.getWahlZiel());

    var gruppenQuelle = findeGruppenNachWahl(quelle);
    var gruppenZiel = findeGruppenNachWahl(ziel);

    return uebernehmeGruppen(ziel, gruppenQuelle, gruppenZiel,
        gruppeUebernehmenParameter.isGruppenAktualisierenWennSchonVorhanden(), gruppeUebernehmenParameter.isUeberzaehligeGruppenLoeschen());
  }

  private List<PolitischeGruppe> findeGruppenNachWahl(Wahl quelle) {
    return politischeGruppeDAO.findByWahl(quelle).stream().filter(g -> !g.isEinzelbewerber()/*ELECT-15325*/).collect(Collectors.toList());
  }

  private GruppeUebernehmenResult uebernehmeGruppen(Wahl zielWahl, List<PolitischeGruppe> gruppenQuelle, List<PolitischeGruppe> gruppenZiel, boolean aktualisieren, boolean ueberzaehligeLoeschen) {
    var gruppenNachTKZiel = gruppenZiel.stream().collect(toMap(PolitischeGruppe::getPolitischeGruppeTK, identity()));

    Map<PolitischeGruppeTK, PolitischeGruppe> zuLoeschendeGruppen = new HashMap<>();
    if (ueberzaehligeLoeschen) {
      for (var zielGruppe : gruppenZiel) {
        zuLoeschendeGruppen.put(zielGruppe.getPolitischeGruppeTK(), zielGruppe);
      }
    }

    var anzahlUebernommenerGruppem = 0L;
    var anzahlBereitsVorhandenerGruppen = 0L;
    var anzahlAktualisierterGruppen = 0L;
    var anzahlGeloeschterGruppen = 0L;

    for (var quelle : gruppenQuelle) {
      zuLoeschendeGruppen.remove(quelle.getPolitischeGruppeTK());

      var tk = quelle.getPolitischeGruppeTK();

      if (gruppenNachTKZiel.containsKey(tk)) {
        if (aktualisieren) {
          var bisherigeGruppe = gruppenNachTKZiel.get(tk);
          aktualisiere(bisherigeGruppe, quelle);
          anzahlAktualisierterGruppen++;
        } else {
          anzahlBereitsVorhandenerGruppen++;
        }
      } else {
        var neueZielGruppe = politischeGruppeFactory.create();
        kopiereGruppeFuerWahl(quelle, neueZielGruppe, zielWahl);
        persistiereNeueGruppe(neueZielGruppe, gruppenNachTKZiel);

        anzahlUebernommenerGruppem++;
      }
    }

    if (!zuLoeschendeGruppen.isEmpty()) {
      politischeGruppeDAO.flush();
      politischeGruppeDAO.clear();
      for (var ueberzaehligeGruppeNotAttached : zuLoeschendeGruppen.values()) {
        var ueberzaehligeGruppe = politischeGruppeDAO.attach(ueberzaehligeGruppeNotAttached);
        anzahlGeloeschterGruppen++;
        gruppeEdit.delete(ueberzaehligeGruppe);
      }
    }

    return new GruppeUebernehmenResult(anzahlUebernommenerGruppem, anzahlBereitsVorhandenerGruppen,
        anzahlAktualisierterGruppen, anzahlGeloeschterGruppen);
  }

  private void aktualisiere(PolitischeGruppe ziel, PolitischeGruppe quelle) {
    quelle.copyEntityAttributesUsingSetter(ziel);
  }

  protected Map<String, Object> createPostCreateContext(Wahl zielWahl) {
    return new HashMap<>();
  }

  private void persistiereNeueGruppe(PolitischeGruppe neueGruppe, Map<PolitischeGruppeTK, PolitischeGruppe> politischeGruppeTKPolitischeGruppeMap) {
    politischeGruppeCreate.save(neueGruppe);
    politischeGruppeTKPolitischeGruppeMap.put(neueGruppe.getPolitischeGruppeTK(), neueGruppe);
  }

  private void kopiereGruppeFuerWahl(PolitischeGruppe quelle, PolitischeGruppe ziel, Wahl zielWahl) {
    ziel.setWahl(zielWahl);
    aktualisiere(ziel, quelle);
  }

}
