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

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;

import de.ivu.elect.business.gebietsbaum.control.GebietsbaumExchangeContext;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.GebietsartBezirksart;
import de.ivu.elect.jee.exchange.Violations;


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

public abstract class AbstractGebietsbaumImportAktualisierer implements GebietsbaumImportAktualisierer {

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient GebietsartDAO gebietsartDAO;

  public AbstractGebietsbaumImportAktualisierer() {
    super();
  }

  @Override
  public boolean isWeitereValidierungErforderlich() {
    return false;
  }

  protected void validiereGebietInDbExists(Violations violations, GebietsbaumExchangeContext context, Gebiet gebietExtern,
      Collection<GebietsartBezirksart> gebietsartBezirksartenExpected) {
    String nummer = gebietExtern.getNummer();
    String gebietsartName = gebietExtern.getGebietsart().getName();

    List<Gebietsart> gebietsarten = gebietsartDAO.findByName(context.getMandant(), gebietsartName);

    if (gebietsarten.size() != 1) {
      violations.addError("gebiet.import.aktualisierer.gebietsart.nicht.gefunden", gebietsartName);

      return;
    }

    Gebietsart gebietsartInDb = gebietsarten.get(0);

    if (!gebietsartBezirksartenExpected.contains(gebietsartInDb.getBezirksart())) {
      violations.addError("gebiet.import.aktualisierer.gebietsart.nicht.bezirksart", gebietsartName,
          gebietsartBezirksartenExpected.stream().map(GebietsartBezirksart::name).collect(joining(", ")));

      return;
    }

    try {
      if (context.hasWahl()) {
        gebietDAO.findByWahlAndNummerAndGebietsart(context.getWahl(), nummer, gebietsartInDb);
      } else if (context.hasWahltermin()) {
        gebietDAO.findByWahlterminAndNummerAndGebietsart(context.getWahltermin(), nummer, gebietsartInDb);
      }
    } catch (NoResultException | NonUniqueResultException ignored) {
      violations.addError("gebiet.import.aktualisierer.gebiet.nicht.gefunden", gebietsartName, nummer);

      return;
    }
  }

  @Override
  public void aktualisiereGebiete(GebietsbaumExchangeContext context, List<Gebiet> gebieteInDb, List<Gebiet> gebieteExternImportiert) {
    aktualisiereGebiete(context, gebieteInDb.stream().collect(Collectors.groupingBy(Gebiet::getNummer)), gebieteExternImportiert);
  }

  protected abstract void aktualisiereGebiete(GebietsbaumExchangeContext context, Map<String, List<Gebiet>> gebieteInDb,
      List<Gebiet> gebieteExternImportiert);

  protected List<Gebiet> filterGebieteByNummerAndGebietsart(Map<String, List<Gebiet>> gebietePerNummer, Gebiet template) {
    return gebietePerNummer.get(template.getNummer()).stream() //
        .filter(gebiet -> attach(gebiet).getGebietsart().getName().equals(template.getGebietsart().getName())) //
        .collect(Collectors.toList());
  }

  protected Gebiet attach(Gebiet gebiet) {
    return gebietDAO.attach(gebiet);
  }
}
