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

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;

import javax.ejb.Stateless;
import javax.inject.Inject;

import de.ivu.elect.business.datei.entity.DateiImportBerichtCustomData;
import de.ivu.elect.business.exchange.control.AbstractEntityImporter;
import de.ivu.elect.business.exchange.control.ImportExchange;
import de.ivu.elect.business.exchange.entity.ImportUpdateReport;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppeimgebiet.control.AbstractGruppeImGebietCache;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietCacheFactory;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietEinzelbewerberValidator;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietExchangeContext;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietGruppenGebieteValidator;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietImportExchange;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietMeta;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietTK;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.entity.Meta;
import de.ivu.elect.jee.exchange.ImportOptions;
import de.ivu.elect.jee.exchange.Violations;

@Stateless
public class GruppeImGebietImporter extends AbstractEntityImporter<GruppeImGebiet, GruppeImGebietTK, GruppeImGebietExchangeContext, DateiImportBerichtCustomData> {

  private static final String MESSAGE_KEY_KEINE_VORHANDEN = "gruppe.im.gebiet.reihenfolgen.bericht.hinweis.keine.vorhanden";

  private static final String MESSAGE_KEY_HINWEIS_UEBERSCHRIEBEN = "gruppe.im.gebiet.reihenfolgen.bericht.hinweis.ueberschrieben";

  private static final String MESSAGE_KEY_ANZAHLEN_PRO_GEBIETSART = "gruppe.im.gebiet.reihenfolgen.bericht.anzahlen.pro.gebietsart";

  private static final String MESSAGE_KEY_ANZAHLEN_GESAMT = "gruppe.im.gebiet.reihenfolgen.bericht.anzahlen.gesamt";

  @Inject
  transient WahlDAO wahlDao;

  @Inject
  transient GruppeImGebietDAO gruppeImGebietDao;

  @Inject
  private transient GruppeImGebietMeta gruppeImGebietMeta;

  @Inject
  private transient GruppeImGebietImportExchange gruppeImGebietImportExchange;

  @Inject
  private transient GruppeImGebietCacheFactory gruppeImGebietCacheFactory;

  @Inject
  transient GruppeImGebietGruppenGebieteValidator gruppeImGebietGruppenGebieteValidator;

  @Inject
  transient GruppeImGebietEinzelbewerberValidator gruppeImGebietEinzelbewerberValidator;

  @Override
  protected void persistiere(Collection<GruppeImGebiet> neueEntities, GruppeImGebietExchangeContext context, DateiImportBerichtCustomData customDateiImportBerichtCustomData, StringBuilder bericht) {
    Wahl wahl = context.getWahl();
    AbstractGruppeImGebietCache gruppeImGebietCache = gruppeImGebietCacheFactory.createNewGruppeImGebietCache(wahl);

    for (GruppeImGebiet gruppeImGebietInDatei : neueEntities) {
      // GruppeImGebiet für Systemgruppen sind fest vorgegeben und werden deshalb nicht importiert
      if (!gruppeImGebietInDatei.isSystemGruppe()) {
        gruppeImGebietCache.persistTauscheGruppeUndGebiet(gruppeImGebietInDatei);
      }
    }
  }

  @Override
  protected void aktualisiere(GruppeImGebiet entityInDatei, GruppeImGebiet entityInDatenbank, GruppeImGebietExchangeContext context, DateiImportBerichtCustomData customDateiImportBerichtCustomData,
      StringBuilder bericht) {
    // GruppeImGebiet für Systemgruppen sind fest vorgegeben und werden deshalb nicht importiert
    if (!entityInDatenbank.isSystemGruppe()) {
      setzePosition(entityInDatenbank, entityInDatei.getRelativePosition());
    }
  }

  protected void setzePosition(GruppeImGebiet entityInDatenbank, Long relativePosition) {
    entityInDatenbank.setRelativePosition(relativePosition);
  }

  @Override
  protected void loescheAusDatenbank(Collection<GruppeImGebiet> entitiesInDb, GruppeImGebietExchangeContext context, DateiImportBerichtCustomData customDateiImportBerichtCustomData,
      StringBuilder bericht) {
    // Löschen besteht bei den Positionen darin, die Position auf null zu setzen
    for (GruppeImGebiet gruppeImGebiet : entitiesInDb) {
      // GruppeImGebiet für Systemgruppen sind fest vorgegeben und werden deshalb nicht importiert
      if (!gruppeImGebiet.isSystemGruppe()) {
        setzePosition(gruppeImGebiet, null);
      }
    }
  }

  @Override
  protected List<GruppeImGebiet> getAllEntitiesInDB(GruppeImGebietExchangeContext context) {
    Wahl wahl = wahlDao.attach(context.getWahl());
    return gruppeImGebietDao.findByWahlAndUngleichGruppeArtEager(wahl, GruppeArt.SYSTEM_GRUPPE);
  }

  @Override
  protected Function<GruppeImGebiet, GruppeImGebietTK> getEntityToEntityTKFunction() {
    return GruppeImGebietTK.getGruppeImGebietToTKFunction();
  }

  @Override
  protected void validiere(Violations violations, List<GruppeImGebiet> importObject, GruppeImGebietExchangeContext context, ImportOptions importOptions) {

    Wahl wahl = wahlDao.attach(context.getWahl());

    for (GruppeImGebiet gruppeImGebiet : importObject) {
      validateEntity(violations, gruppeImGebiet, gruppeImGebiet.getGruppeImGebietTK());
    }

    gruppeImGebietGruppenGebieteValidator.pruefeExistenzAlleGruppenUndGebiete(importObject, wahl, violations);
    gruppeImGebietEinzelbewerberValidator.pruefeEinzelbewerberNurAufEinemWahlgebiet(importObject, wahl, violations);
    gruppeImGebietEinzelbewerberValidator.pruefeEinzelbewerberAufKeinemAnderemWahlgebietInDB(importObject, wahl, violations);
  }

  @Override
  protected void erstelleBericht(List<GruppeImGebiet> importObject, GruppeImGebietExchangeContext context, ImportOptions importOptions, StringBuilder bericht, boolean vorab) {
    if (getAnzahlMitPosition(getAllEntitiesInDB(context)) > 0) {
      bericht.append(getMessage(MESSAGE_KEY_HINWEIS_UEBERSCHRIEBEN));
    } else {
      bericht.append(getMessage(MESSAGE_KEY_KEINE_VORHANDEN));
    }

    bericht.append("\n\n");

    Map<String, Set<Gebiet>> gebietePerGebietsartName = new HashMap<>();
    Map<String, Integer> anzahlGebietePerGebietsartName = new HashMap<>();

    Set<Gebiet> alleGebiete = new HashSet<>();

    for (GruppeImGebiet gruppeImGebiet : importObject) {
      Gebiet gebiet = gruppeImGebiet.getGebiet();
      String gebietsartName = gebiet.getGebietsart().getName();

      Set<Gebiet> gebiete = gebietePerGebietsartName.get(gebietsartName);

      if (gebiete == null) {
        gebiete = new HashSet<>();
        gebietePerGebietsartName.put(gebietsartName, gebiete);
        anzahlGebietePerGebietsartName.put(gebietsartName, 1);
      } else {
        anzahlGebietePerGebietsartName.put(gebietsartName, anzahlGebietePerGebietsartName.get(gebietsartName) + 1);
      }

      gebiete.add(gebiet);
      alleGebiete.add(gebiet);
    }

    bericht.append(getMessage(MESSAGE_KEY_ANZAHLEN_GESAMT, importObject.size(), alleGebiete.size()));
    bericht.append('\n');

    for (Entry<String, Set<Gebiet>> entry : gebietePerGebietsartName.entrySet()) {
      String gebietsartName = entry.getKey();

      bericht.append(getMessage(MESSAGE_KEY_ANZAHLEN_PRO_GEBIETSART, anzahlGebietePerGebietsartName.get(gebietsartName), entry.getValue().size(), gebietsartName));
      bericht.append('\n');
    }
  }

  @Override
  protected ImportExchange<List<GruppeImGebiet>, GruppeImGebietExchangeContext> getExchange() {
    return gruppeImGebietImportExchange;
  }

  @Override
  public Meta getMetaForEntityInternal() {
    return gruppeImGebietMeta;
  }

  @Override
  protected ImportUpdateReport createImportUpdateReport(List<GruppeImGebiet> inDatei, Violations violations, GruppeImGebietExchangeContext context, ImportOptions importOptions,
      StringBuilder bericht) {
    int anzahlMitPositionInDB = getAnzahlMitPosition(getAllEntitiesInDB(context));
    int anzahlMitPositionInDatei = getAnzahlMitPosition(inDatei);

    return new ImportUpdateReport(violations, anzahlMitPositionInDatei, anzahlMitPositionInDB, 0, importOptions, bericht);
  }

  private int getAnzahlMitPosition(List<GruppeImGebiet> gigs) {
    return (int) gigs.stream().filter(gruppeImGebiet -> gruppeImGebiet.getRelativePosition() != null).count();
  }
}
