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

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

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.JPQLQuery;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.kandidatur.control.KandidaturByGebietStimmzettelPositionNameComparator;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.QKandidatur;
import de.ivu.elect.business.person.entity.Beruf;
import de.ivu.elect.business.person.entity.QBeruf;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenExtendedDAOFactory;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.entity.QueryDsl;
import de.ivu.elect.jee.presentation.dynamicform.BooleanFilterKriterium;

public class KandidaturSearch extends AbstractKandidaturSearch {

  private static final Logger LOG = LoggerFactory.getLogger(KandidaturSearch.class);

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient EntityAufStimmzettelDatenExtendedDAOFactory entityAufStimmzettelDatenExtendedDAOFactory;

  public KandidaturSearch() {
    super();
  }

  @Override
  protected List<Kandidatur> querySearchResult(KandidaturSearchParameters searchParameters) {
    Gebiet gebiet = gebietExtendedDAO.attach(searchParameters.getGebiet());

    QKandidatur kandidatur = QKandidatur.kandidatur;

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

    Wahl wahl = gebiet.getWahl();

    queryDsl.eq(kandidatur.wahl, wahl);
    queryDsl.eqIfNotNull(kandidatur.kandidaturStatus, searchParameters.getKandidaturStatus());
    queryDsl.eqIfNotNull(kandidatur.personStatus, searchParameters.getPersonStatus());

    queryDsl.likeNormalizedIfNotBlank(kandidatur.familiennameNormalisiert, searchParameters.getFamilienname());
    queryDsl.likeInAnyNormalizedIfNotBlank(kandidatur.rufnameNormalisiert, kandidatur.vornamenNormalisiert, searchParameters.getVornameSeparator(), searchParameters.getVorname());

    BooleanFilterKriterium berufsschluesselVorhanden = searchParameters.getBerufsschluesselVorhanden();

    if (berufsschluesselVorhanden != null) {
      if (berufsschluesselVorhanden.toBoolean()) {
        queryDsl.isNotNull(kandidatur.berufsschluessel);
      } else {
        queryDsl.isNull(kandidatur.berufsschluessel);
      }
    }

    BooleanFilterKriterium freieBerufsangabeFilterKriterium = searchParameters.getFreieBerufsangabe();

    if (freieBerufsangabeFilterKriterium != null) {
      QBeruf beruf = QBeruf.beruf;
      JPQLQuery<Beruf> berufSubQuery = JPAExpressions.selectFrom(beruf).where(beruf.name.eq(kandidatur.berufUnformatiert));

      if (freieBerufsangabeFilterKriterium.toBoolean()) {
        queryDsl.getQuery().where(berufSubQuery.notExists());
      } else {
        queryDsl.getQuery().where(berufSubQuery.exists());
      }
    }

    List<Kandidatur> result = queryDsl.list(kandidatur);

    result = filtereGebiete(result, gebiet);
    result = filterePolitischeGruppe(result, searchParameters.getGruppe());

    result = sortiere(gebiet, entityAufStimmzettelDatenExtendedDAOFactory.getEntityAufStimmzettelDaten(), result);

    return result;
  }

  protected List<Kandidatur> sortiere(Gebiet gebiet, EntityAufStimmzettelDaten entityAufStimmzettelDaten, List<Kandidatur> list) {
    if (list.isEmpty()) {
      return list;
    }

    List<Kandidatur> result = new ArrayList<>(list);
    result.sort(new KandidaturByGebietStimmzettelPositionNameComparator(gebiet, entityAufStimmzettelDaten));

    return result;
  }

  private List<Kandidatur> filterePolitischeGruppe(List<Kandidatur> list, PolitischeGruppe politischeGruppe) {
    if (politischeGruppe == null) {
      return list;
    }

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

    for (Kandidatur kandidatur : list) {
      if (kandidatur.getGruppe().equals(politischeGruppe)) {
        result.add(kandidatur);
      }
    }

    return result;
  }

  protected List<Kandidatur> filtereGebiete(List<Kandidatur> list, Gebiet gebiet) {
    return list.stream().filter(kandidatur -> isGebietPassend(kandidatur, gebiet)).collect(Collectors.toList());
  }

  private boolean isGebietPassend(Kandidatur kandidatur, Gebiet gebiet) {
    if (kandidatur.getWahlvorschlag() == null) {
      LOG.warn("Kandidatur ohne Wahlvorschlag vorhanden: " + kandidatur);
      return false;
    }

    if (kandidatur.getWahlvorschlag().getGebiet() == null) {
      LOG.warn("Kandidatur mit Wahlvorschlag ohne Gebiet vorhanden: " + kandidatur + ", " + kandidatur.getWahlvorschlag());
      return false;
    }

    Gebiet kandidaturGebiet = kandidatur.getWahlvorschlag().getGebiet();

    return gebiet.equals(kandidaturGebiet) || kandidaturGebiet.isUntergebietVon(gebiet);
  }
}
