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

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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.control.GebietByPositionComparator;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsgruppierung;
import de.ivu.elect.business.gebietsbaum.entity.GebietsgruppierungStatusResult;
import de.ivu.elect.business.wahl.entity.Wahl;


import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

public class GebietsgruppierungStatus implements Serializable {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient GebietsgruppierungExtendedDAO gebietsgruppierungDAO;

  @Inject
  private transient GebietExtendedDAO gebietDAO;

  public List<String> getArten(Wahl wahlNotAttached) {
    if (wahlNotAttached == null) {
      return emptyList();
    }

    return gebietsgruppierungDAO.findByWahl(wahlNotAttached).stream().map(Gebietsgruppierung::getGebietsgruppierungArt).distinct().sorted()
        .collect(Collectors.toList());
  }

  public GebietsgruppierungStatusResult getGebietsgruppierungStatus(Wahl wahlNotAttached, String art) {

    if (StringUtils.isBlank(art) || wahlNotAttached == null) {
      return GebietsgruppierungStatusResult.EMPTY;
    }

    List<Gebietsgruppierung> gebietsgruppierungen = gebietsgruppierungDAO.findByWahlAndGebietsgruppierungArten(wahlNotAttached, singletonList(art));
    if (gebietsgruppierungen.isEmpty()) {
      return GebietsgruppierungStatusResult.EMPTY;
    }

    List<Gebiet> erfassungsgebiete = loadGebiete(wahlNotAttached);

    Map<String, Long> anzahlErfassungsgebieteProName = new HashMap<>();
    for (Gebietsgruppierung gebietsgruppierung : gebietsgruppierungen) {
      anzahlErfassungsgebieteProName.put(gebietsgruppierung.getName(), 0L);
    }

    Set<Gebiet> gebieteOhneGruppierung = new HashSet<>(erfassungsgebiete);
    Map<Gebiet, List<Gebietsgruppierung>> gebieteMehrfach = new HashMap<>();

    for (Gebiet gebiet : erfassungsgebiete) {
      for (Gebietsgruppierung gebietsgruppierung : gebietsgruppierungen) {
        if (gebietsgruppierung.isEnthaeltGebietOderUebergeordnetesGebietVon(gebiet)) {
          anzahlErfassungsgebieteProName.put(gebietsgruppierung.getName(), anzahlErfassungsgebieteProName.get(gebietsgruppierung.getName()) + 1L);
          gebieteOhneGruppierung.remove(gebiet);
          gebieteMehrfach.computeIfAbsent(gebiet, x -> new ArrayList<>()).add(gebietsgruppierung);
        }
      }
    }

    new HashMap<>(gebieteMehrfach).entrySet().stream().filter(e -> e.getValue().size() < 2).map(Map.Entry::getKey).forEach(gebieteMehrfach::remove);

    ArrayList<Gebiet> gebieteOhneGruppierungSortiert = new ArrayList<>(gebieteOhneGruppierung);
    gebieteOhneGruppierungSortiert.sort(new GebietByPositionComparator());

    return new GebietsgruppierungStatusResult(anzahlErfassungsgebieteProName, gebieteOhneGruppierungSortiert, gebieteMehrfach);

  }

  private List<Gebiet> loadGebiete(Wahl wahlNotAttached) {
    return gebietDAO.findByWahl(wahlNotAttached).stream().peek(g -> {
      // lazy loading
      g.getGebietsart().getGebietsartName();
      g.getNummer();
    }).filter(Gebiet::isErfassungsgebiet)
        .collect(Collectors.toList());
  }

}
