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

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

import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.stimmzettel.control.StimmzettelGebietUndNummerComparator;
import de.ivu.elect.business.stimmzettel.entity.QStimmzettel;
import de.ivu.elect.business.stimmzettel.entity.Stimmzettel;
import de.ivu.elect.business.stimmzettel.entity.StimmzettelGueltigkeit;
import de.ivu.elect.jee.entity.QueryDsl;


import static java.util.stream.Collectors.toList;

public class StimmzettelSearch extends AbstractStimmzettelSearch {


  public static final int MAX_GEBIETE_ANZAHL = 999; // Oracle Limit

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Override
  protected List<Stimmzettel> querySearchResult(StimmzettelSearchParameters searchParameters) {
    if (searchParameters.getGebiet() == null) {
      return Collections.emptyList();
    }

    QStimmzettel qStimmzettel = QStimmzettel.stimmzettel;

    QueryDsl queryDsl = dao.getQueryDsl();
    queryDsl.from(qStimmzettel);
    applyNummerFilter(searchParameters, qStimmzettel, queryDsl);
      queryDsl.eqIfNotNull(qStimmzettel.status, searchParameters.getStimmzettel().getStatus());
    queryDsl.eqIfNotNull(qStimmzettel.stimmzettelTeam, searchParameters.getStimmzettel().getStimmzettelTeam());
    queryDsl.orderBy(qStimmzettel.nummer.asc());


    if (searchParameters.isGueltigkeitPerBeschlussBestimmt()) {
      queryDsl.inIfNotEmpty(qStimmzettel.gueltigkeit, getPerBeschlussGueltigkeiten());
    } else {
       queryDsl.eqIfNotNull(qStimmzettel.gueltigkeit, searchParameters.getStimmzettel().getGueltigkeit());
    }

    Gebiet gebiet = gebietExtendedDAO.attach(searchParameters.getGebiet());
    if (gebiet.isErfassungsgebiet()) {
      queryDsl.eq(qStimmzettel.gebiet, gebiet);
    } else {
      List<Gebiet> gebiete = gebietExtendedDAO.findByWahl(gebiet.getWahl()).stream()
          .filter(AbstractGebiet::isErfassungsgebiet)
          .filter(gebiet::isInGebietshierarchieUebergeordnetesGebietVon).collect(toList());
      if (gebiete.isEmpty()) {
        return Collections.emptyList();
      }
      if (gebiete.size() <= MAX_GEBIETE_ANZAHL) {
        queryDsl.inIfNotEmpty(qStimmzettel.gebiet, gebiete);
      } else {
        if (!gebiet.isWurzelgebiet()) {
          return Collections.emptyList(); // nicht unterstützt
        }
      }
    }

    applyLimit(searchParameters, queryDsl);

    return manuellFilterOrder(queryDsl.list(qStimmzettel), searchParameters);
  }

  private List<StimmzettelGueltigkeit> getPerBeschlussGueltigkeiten() {
    return Arrays.stream(StimmzettelGueltigkeit.values()).filter(StimmzettelGueltigkeit::isPerBeschlussFestgelegt)
        .collect(toList());
  }

  protected void applyLimit(StimmzettelSearchParameters searchParameters, QueryDsl queryDsl) {
    searchParameters.applyLimit(queryDsl);
  }

  protected List<Stimmzettel> manuellFilterOrder(List<Stimmzettel> list, StimmzettelSearchParameters searchParameters) {
    list.sort(StimmzettelGebietUndNummerComparator.INSTANCE);
    return list;
  }

  protected void applyNummerFilter(StimmzettelSearchParameters searchParameters, QStimmzettel qStimmzettel, QueryDsl queryDsl) {
    queryDsl.eqIfNotBlank(qStimmzettel.nummer, searchParameters.getNummer());
  }
}