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

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.statistik.entity.QStatistikKennzeichen;
import de.ivu.elect.business.statistik.entity.StatistikKennzeichen;
import de.ivu.elect.business.statistik.entity.StatistikKennzeichenWahlQualifier;
import de.ivu.elect.business.statistik.entity.StatistikLauf;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.jee.entity.QueryDsl;

public class StatistikKennzeichenSearch extends AbstractStatistikKennzeichenSearch {

  @Inject
  private transient StatistikLaufExtendedDAO statistikLaufExtendedDAO;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;

  public StatistikKennzeichenSearch() {
    super();
  }

  @Override
  protected List<StatistikKennzeichen> querySearchResult(StatistikKennzeichenSearchParameters searchParameters) {
    if (searchParameters.getStatistikLauf() == null || searchParameters.getGebiet() == null) {
      return Collections.emptyList();
    }

    StatistikLauf statistikLauf = statistikLaufExtendedDAO.attach(searchParameters.getStatistikLauf());
    Gebiet gebiet = gebietExtendedDAO.attach(searchParameters.getGebiet());

    QueryDsl queryDsl = dao.getQueryDsl();
    QStatistikKennzeichen qStatistikKennzeichen = QStatistikKennzeichen.statistikKennzeichen;
    queryDsl.from(qStatistikKennzeichen);
    queryDsl.eq(qStatistikKennzeichen.statistikLauf, statistikLauf);

    searchParameters.getGebietauswahl().setQueryDslClause(queryDsl, qStatistikKennzeichen, gebiet);

    if (searchParameters.getWahl() != null) {
      if (StatistikKennzeichenSearchParameters.ALLE_WAHLEN.equals(searchParameters.getWahl())) {
        queryDsl.isNull(qStatistikKennzeichen.wahl);
      } else {
        queryDsl.eq(qStatistikKennzeichen.wahl, wahlExtendedDAO.attach(searchParameters.getWahl()));
      }
    }

    queryDsl.eqIfNotNull(qStatistikKennzeichen.kennzeichen, searchParameters.getStatistikKennzeichen().getKennzeichen());

    queryDsl.orderBy(qStatistikKennzeichen.gebiet.position.asc(), qStatistikKennzeichen.kennzeichen.desc());

    // ELECTMUC-2127 searchParameters.applyLimit(queryDsl);
    List<StatistikKennzeichen> result = queryDsl.list(qStatistikKennzeichen);

    result = searchParameters.getGebietauswahl().filterAndMap(result, gebiet);

    // ELECTMUC-2127
    if (searchParameters.getLimit() != null && result.size() > searchParameters.getLimit()) {
      result = result.subList(0, Math.toIntExact(searchParameters.getLimit()));
    }

    return result;
  }

  public static List<StatistikKennzeichen> kumuliereStatistikKennzeichen(List<StatistikKennzeichen> statistikKennzeichens, Gebiet gebiet) {
    if (statistikKennzeichens.isEmpty()) {
      return statistikKennzeichens;
    }

    Map<StatistikKennzeichenWahlQualifier, List<StatistikKennzeichen>> map = statistikKennzeichens.stream()
        .collect(Collectors.groupingBy(StatistikKennzeichenWahlQualifier::new, mapping(identity(), toList())));

    AtomicLong id = new AtomicLong(Long.MIN_VALUE);
    return map.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getKey)).map(e -> erzeugeKumuliertenEintrag(e.getKey(), e.getValue(), gebiet, id)).collect(toList());
  }

  private static StatistikKennzeichen erzeugeKumuliertenEintrag(StatistikKennzeichenWahlQualifier key, List<StatistikKennzeichen> value, Gebiet gebiet, AtomicLong id) {
    StatistikKennzeichen result = new StatistikKennzeichen() {

      private static final long serialVersionUID = 1L;

      @Override
      public boolean isPersistent() {
        return false; // trotz ID nicht persistent
      }
    };

    result.setId(id.addAndGet(1)); // zu setzen, damit PrimeFaces nicht meckert...
    result.setWahl(key.getWahl());
    result.setKennzeichen(key.getStatistikKennzeichenArt());
    result.setQualifier(key.getQualifier());
    result.setGebiet(gebiet);
    result.setWert(kumuliere(value));

    return result;
  }

  private static Long kumuliere(List<StatistikKennzeichen> value) {
    return value.stream().map(StatistikKennzeichen::getWert).filter(Objects::nonNull).mapToLong(l -> l).sum();
  }
}
