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

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.QErgebniseingang;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungExtendedDAO;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.jee.entity.QueryDsl;

public class ErgebniseingangSearch extends AbstractErgebniseingangSearch {

  @Inject
  private transient WahldurchfuehrungExtendedDAO wahldurchfuehrungExtendedDAO;

  @Inject
  private transient WahldurchfuehrungsgebietExtendedDAO wahldurchfuehrungsgebietExtendedDAO;

  @Inject
  private transient StimmeingabefehlerExtendedDAO stimmeingabefehlerExtendedDAO;

  public ErgebniseingangSearch() {
    super();
  }

  @Override
  protected List<Ergebniseingang> querySearchResult(ErgebniseingangSearchParameters searchParameters) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungExtendedDAO.attach(searchParameters.getWahldurchfuehrung());
    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = wahldurchfuehrungsgebietExtendedDAO.attach(searchParameters.getWahldurchfuehrungsgebiet());

    QErgebniseingang ergebniseingang = QErgebniseingang.ergebniseingang;

    QueryDsl queryDsl = dao.getQueryDsl();
    queryDsl.from(ergebniseingang);

    queryDsl.eq(ergebniseingang.wahldurchfuehrungsgebiet.wahl, wahldurchfuehrung);

    queryDsl.eqIfNotNull(ergebniseingang.ergebniseingangsart, searchParameters.getErgebniseingangsart());
    queryDsl.inIfNotEmpty(ergebniseingang.ergebniseingabestufe, searchParameters.getErgebniseingabestufen());
    queryDsl.inIfNotEmpty(ergebniseingang.ergebniseingangsfehlerstatus, searchParameters.getErgebniseingangsfehlerstatus());

    queryDsl.afterIfNotNull(ergebniseingang.zeitstempel, searchParameters.getZeitpunktVon());
    queryDsl.beforeIfNotNull(ergebniseingang.zeitstempel, searchParameters.getZeitpunktBis());

    queryDsl.joinFetch(ergebniseingang.wahldurchfuehrungsgebiet);
    queryDsl.orderBy(ergebniseingang.eingangssequenznummer.desc());

    return filterNurLetztenEingangProGebiet(filterFehlerWarnungn(filterGebiet(queryDsl.list(ergebniseingang), wahldurchfuehrungsgebiet),
        searchParameters.getFehlerWarnungen()),
        searchParameters.isNurLetztenEingangProGebietAnzeigen());
  }

  private List<Ergebniseingang> filterFehlerWarnungn(List<Ergebniseingang> list, String fehlerWarnungen) {
    if (StringUtils.isBlank(fehlerWarnungen)) {
      return list;
    }

    return list.stream().filter(e->trifftFehlerWarnung(e, fehlerWarnungen)).collect(Collectors.toList());
  }

  private boolean trifftFehlerWarnung(Ergebniseingang e, String fehlerWarnungen) {
    return e.getStimmeingabefehlerBeschreibungen(stimmeingabefehlerExtendedDAO).stream().anyMatch(s -> s != null && s.contains(fehlerWarnungen));
  }

  private List<Ergebniseingang> filterGebiet(List<Ergebniseingang> list, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    if (wahldurchfuehrungsgebiet == null || wahldurchfuehrungsgebiet.getUebergeordnetesGebiet() == null) {
      return list; // Kein Filtern notwendig
    }

    return list.stream()
        .filter(ergebniseingang -> ergebniseingang.getWahldurchfuehrungsgebiet().isInGebietshierarchieUntergeordnetesGebietVon(wahldurchfuehrungsgebiet))
        .collect(Collectors.toList());
  }

  private List<Ergebniseingang> filterNurLetztenEingangProGebiet(List<Ergebniseingang> list, boolean nurLetztenEingangProGebietAnzeigen) {
    if (!nurLetztenEingangProGebietAnzeigen) {
      return list;
    }

    Set<Wahldurchfuehrungsgebiet> betrachteteGebiete = new HashSet<>();

    List<Ergebniseingang> result = new ArrayList<>();

    for (Ergebniseingang ergebniseingang : list) {
      if (!betrachteteGebiete.contains(ergebniseingang.getWahldurchfuehrungsgebiet())) {
        result.add(ergebniseingang);
        betrachteteGebiete.add(ergebniseingang.getWahldurchfuehrungsgebiet());
      }
    }

    return result;
  }
}
