package de.ivu.elect.presentation.gebietsbaum.tree;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Nullable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.boundary.LetzterEingangLoader;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsfehlerstatus;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.presentation.security.GebietSecurity;
import de.ivu.elect.presentation.stimmeingabe.StimmeingabeErfassenViewErgebniseingangsartStore;

@RequestScoped
public class WasGebietsbaumDynamicStore implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  protected StimmeingabeErfassenViewErgebniseingangsartStore stimmeingabeErfassenViewErgebniseingangsartStore;

  @Inject
  private transient WahldurchfuehrungsgebietExtendedDAO wahldurchfuehrungsgebietExtendedDAO;

  @Inject
  protected transient GebietSecurity gebietSecurity;

  @Inject
  private transient LetzterEingangLoader letzterEingangLoader;

  private Ergebniseingangsart ergebniseingangsart;

  private Map<GebietPK, List<GebietPK>> erfassungsgebieteFuerGebiet;

  private final Map<GebietPK, WasGebietsbaumErfassungStatus> erfassungsstatusFuerGebiet = new HashMap<>();

  public WasGebietsbaumErfassungStatus getWasGebietsbaumErfassungStatus(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return erfassungsstatusFuerGebiet.computeIfAbsent(wahldurchfuehrungsgebiet.getPk(), x -> berechneWasGebietsbaumErfassungStatus(wahldurchfuehrungsgebiet));
  }

  private WasGebietsbaumErfassungStatus berechneWasGebietsbaumErfassungStatus(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (isEinsehenRechtVorhanden(wahldurchfuehrungsgebiet)) {
      if (wahldurchfuehrungsgebiet.isErfassungsgebietLautGebietsart()) {
        return new WasGebietsbaumErfassungStatus(bestimmeErfassungsgebietErgebniseingangsfehlerstatus(wahldurchfuehrungsgebiet), bestimmeAusbleibend(wahldurchfuehrungsgebiet));
      }

      Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl = new EnumMap<>(Ergebniseingangsfehlerstatus.class);

      List<GebietPK> untergeordneteErfassungsgebiete = getUntergeordneteErfassungsgebiete(wahldurchfuehrungsgebiet);
      for (GebietPK gebietPK : untergeordneteErfassungsgebiete) {
        Ergebniseingangsfehlerstatus ergebniseingangsfehlerstatus = bestimmeErfassungsgebietErgebniseingangsfehlerstatus(gebietPK);
        if (ergebniseingangsfehlerstatus != null) {
          ergebniseingangsfehlerstatusAnzahl
              .put(ergebniseingangsfehlerstatus, ergebniseingangsfehlerstatusAnzahl.computeIfAbsent(ergebniseingangsfehlerstatus, x -> 0L) + 1L);
        }
      }
      return new WasGebietsbaumErfassungStatus(ergebniseingangsfehlerstatusAnzahl, untergeordneteErfassungsgebiete.size());
    } else {
      return new WasGebietsbaumErfassungStatus(Collections.emptyMap(), 0L);
    }

  }

  private WasGebietsbaumAusbleibend bestimmeAusbleibend(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (wahldurchfuehrungsgebiet.getErgebniseingabestatus(getErgebniseingangsart(wahldurchfuehrungsgebiet)).isAusbleibend()) {
      if (wahldurchfuehrungsgebiet.getErfasstIn() != null) {
        return WasGebietsbaumAusbleibend.AUSBLEIBEND_ANDERES_GEBIET;
      }
      return WasGebietsbaumAusbleibend.AUSBLEIBEND;
    }

    return WasGebietsbaumAusbleibend.KEIN;
  }

  private boolean isEinsehenRechtVorhanden(Gebiet gebiet) {
    return gebietSecurity.getEinsehenGebietSecurity().isRechtVorhanden(gebiet);
  }

  private List<GebietPK> getUntergeordneteErfassungsgebiete(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (erfassungsgebieteFuerGebiet == null) {

      erfassungsgebieteFuerGebiet = new HashMap<>();
      Wahl wahl = wahldurchfuehrungsgebiet.getWahl();
      if (wahl != null) {
        List<Wahldurchfuehrungsgebiet> alleGebiete = wahldurchfuehrungsgebietExtendedDAO.findByWahl(wahl);
        for (Wahldurchfuehrungsgebiet gebiet : alleGebiete) {
          if (gebiet.isErfassungsgebiet() && isEinsehenRechtVorhanden(gebiet)) {
            putObergebiete(gebiet, gebiet, erfassungsgebieteFuerGebiet, alleGebiete);
          }
        }
      }
    }
    return erfassungsgebieteFuerGebiet.getOrDefault(wahldurchfuehrungsgebiet.getPk(), Collections.emptyList());
  }

  private void putObergebiete(Wahldurchfuehrungsgebiet gebiet, Wahldurchfuehrungsgebiet erfassungsgebiet,
                              Map<GebietPK, List<GebietPK>> erfassungsgebieteFuerGebiet, List<Wahldurchfuehrungsgebiet> alleGebiete) {
    Gebiet uebergeordnetesGebiet = gebiet.getUebergeordnetesGebiet();
    if (uebergeordnetesGebiet != null && isEinsehenRechtVorhanden(uebergeordnetesGebiet)) {
      Wahldurchfuehrungsgebiet uebergeordnetesWahldurchfuehrungsgebiet = uebergeordnetesGebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity();
      erfassungsgebieteFuerGebiet.computeIfAbsent(uebergeordnetesWahldurchfuehrungsgebiet.getPk(), x -> new ArrayList<>(alleGebiete.size()))
          .add(erfassungsgebiet.getPk());
      putObergebiete(uebergeordnetesWahldurchfuehrungsgebiet, erfassungsgebiet, erfassungsgebieteFuerGebiet, alleGebiete);
    }
  }

  @Nullable
  private Ergebniseingangsfehlerstatus bestimmeErfassungsgebietErgebniseingangsfehlerstatus(GebietPK gebietPK) {
    return bestimmeErfassungsgebietErgebniseingangsfehlerstatus(wahldurchfuehrungsgebietExtendedDAO.findByPrimaryKey(gebietPK));
  }

  @Nullable
  private Ergebniseingangsfehlerstatus bestimmeErfassungsgebietErgebniseingangsfehlerstatus(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return letzterEingangLoader.bestimmeLetztenEingang(wahldurchfuehrungsgebiet.getWahl().getPk(), wahldurchfuehrungsgebiet.getPk(),
        getErgebniseingangsart(wahldurchfuehrungsgebiet)).getErgebniseingangsfehlerstatus();
//    Ergebniseingang ergebniseingang = wahldurchfuehrungsgebiet.getErgebniseingang(getErgebnisart(wahldurchfuehrungsgebiet));
//    if (ergebniseingang != null) {
//      return ergebniseingang.getErgebniseingangsfehlerstatus();
//    }
//
//    return null;

  }

  private Ergebniseingangsart getErgebniseingangsart(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (ergebniseingangsart == null) {
      ergebniseingangsart = stimmeingabeErfassenViewErgebniseingangsartStore.getErgebniseingangsart(wahldurchfuehrungsgebiet.getWahl());
    }
    return ergebniseingangsart;
  }

}
