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

import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.boundary.GruppeDAO;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.boundary.WahlvorschlagDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.jee.exception.ElectCommonException;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.NoResultException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Stateless
public class GruppeImGebietHierarchyCleaner {

  @Inject
  private transient GruppeDAO gruppeDao;

  @Inject
  private transient GebietDAO gebietDao;

  @Inject
  private transient GruppeImGebietDAO gruppeImGebietDao;

  @Inject
  private transient WahlvorschlagDAO wahlvorschlagDAO;

  /**
   * Löscht alle GruppeImGebiet einer Wahl, die in der über- und untergeordneten Gebietshierarchie keinen Wahlvorschlag haben.
   */
  public void entferneVerwaisteGruppeImGebiet(Wahl wahl) {
    if (wahl == null) {
      throw new ElectCommonException("wahl.selected.missing");
    }
    try {
      Gebiet wurzelgebiet = gebietDao.findByWahlWurzelgebiet(wahl);
      for (Gruppe gruppe : gruppeDao.findByWahl(wahl)) {
        try {
          GruppeImGebiet gruppeImGebiet = gruppeImGebietDao.findByGruppeAndGebiet(gruppe, wurzelgebiet);
          List<Wahlvorschlag> wahlvorschlaegeDerGruppe = wahlvorschlagDAO.findByGruppe(gruppe);

          cleanHierarchy(gruppeImGebiet, wahlvorschlaegeDerGruppe);

        } catch (NoResultException e) {
          // für die Gruppe existiert keine GruppeImGebiet
        }
      }
    } catch (NoResultException e) {
      // kein Wurzelgebiet vorhanden -> keine GruppeImGebiet vorhanden
    }
  }

  public void cleanHierarchy(GruppeImGebiet gruppeImGebiet, List<Wahlvorschlag> vorhandeneWahlvorschlaegeDerGruppe) {
    cleanHierarchyRecursive(gruppeImGebiet, vorhandeneWahlvorschlaegeDerGruppe, new HashSet<>());
  }

  private void cleanHierarchyRecursive(GruppeImGebiet gruppeImGebiet, List<Wahlvorschlag> wahlvorschlaegeDerGruppe, Set<Gebiet> betrachteteGebiete) {
    Gebiet gebiet = gruppeImGebiet.getGebiet();
    if (betrachteteGebiete.contains(gebiet)) {
      return; // Gebiet wurde bereits bearbeitet.
    }
    betrachteteGebiete.add(gebiet);

    Gruppe gruppe = gruppeImGebiet.getGruppe();
    if (wahlvorschlaegeDerGruppe.stream().anyMatch(wahlvorschlag -> gebiet.isInGebietshierarchieUebergeordnetesGebietVon(wahlvorschlag.getGebiet())
        || wahlvorschlag.getGebiet().isInGebietshierarchieUebergeordnetesGebietVon(gebiet))) {
      return; // Es existieren noch relevante Wahlvorschläge in der Hierarchie.
    }

    // Untergeordnete Hierarchie löschen...
    List<GruppeImGebiet> zuLoeschendeGruppeImGebiet = new ArrayList<>();
    zuLoeschendeGruppeImGebiet.add(gruppeImGebiet);
    for (Gebiet untergeorndetesGebiet : gebiet.getUntergeordneteGebiete(gebietDao)) {
      if (!betrachteteGebiete.contains(untergeorndetesGebiet)) {
        try {
          GruppeImGebiet untergeordneteGruppeImGebiet = gruppeImGebietDao.findByGruppeAndGebiet(gruppe, untergeorndetesGebiet);
          zuLoeschendeGruppeImGebiet.add(untergeordneteGruppeImGebiet);
        } catch (NoResultException e) {
          // keine untergeordnete GruppeImGebiet gefunden
        }
      }
    }
    zuLoeschendeGruppeImGebiet.forEach(gruppeImGebietDao::remove);

    // Übergeordnete Hierarchie aufräumen...
    if (gebiet.hasUebergeordnetesGebiet()) {
      try {
        GruppeImGebiet uebergeordneteGruppeImGebiet = gruppeImGebietDao.findByGruppeAndGebiet(gruppe, gebiet.getUebergeordnetesGebiet());
        cleanHierarchyRecursive(uebergeordneteGruppeImGebiet, wahlvorschlaegeDerGruppe, betrachteteGebiete);
      } catch (NoResultException e) {
        // keine übergeordnete GruppeImGebiet gefunden
      }
    }
  }

}
