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

import static java.util.Arrays.asList;
import static java.util.Objects.isNull;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

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.control.NoContext;
import de.ivu.elect.business.geodata.control.GeodataOrtImportExchange;
import de.ivu.elect.business.geodata.entity.GeodataOrt;
import de.ivu.elect.business.geodata.entity.GeodataOrtMeta;
import de.ivu.elect.business.geodata.entity.GeodataOrtUK;
import de.ivu.elect.business.geodata.entity.GeodataPlz;
import de.ivu.elect.business.geodata.entity.GeodataPlzUK;
import de.ivu.elect.jee.entity.Meta;
import de.ivu.elect.jee.exchange.ImportOptions;
import de.ivu.elect.jee.exchange.Violations;

@Stateless
public class GeodataOrtImporter extends AbstractEntityImporter<GeodataOrt, GeodataOrtUK, NoContext, DateiImportBerichtCustomData> {

  @Inject
  private transient GeodataOrtImportExchange geodataOrtImportExchange;

  @Inject
  private transient GeodataOrtDAO geodataOrtDao;

  @Inject
  private transient GeodataOrtMeta geodataOrtMeta;

  @Inject
  private transient GeodataPlzDAO geodataPlzDao;

  private Map<GeodataPlzUK, GeodataPlz> geodataPlzUkPerGeodataPlz;

  public GeodataOrtImporter() {
    super();
  }

  /**
   * Man könnte alle ImportOptions unterstützen, ist aber fachlich eher verwirrend. Deshalb die Einschränkung.
   */
  @Override
  public Collection<ImportOptions> getSupportedImportOptions(NoContext context) {
    return asList(ImportOptions.IMPORT_VOLLSTAENDIG_UEBERNEHMEN, ImportOptions.NEUE_DATEN_ANLEGEN_BESTEHENDE_DATEN_UNVERAENDERT_LASSEN);
  }

  @Override
  protected ImportExchange<List<GeodataOrt>, NoContext> getExchange() {
    return geodataOrtImportExchange;
  }

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

  @Override
  protected void validiere(Violations violations, List<GeodataOrt> geodataOrte, NoContext context, ImportOptions importOptions) {
    geodataOrte.forEach(geodataOrt -> validateEntity(violations, geodataOrt, geodataOrt.getGeodataOrtTK()));
  }

  @Override
  protected void persistiere(Collection<GeodataOrt> geodataOrteNurInDateiNichtInDb, NoContext context,
      DateiImportBerichtCustomData dateiImportBerichtCustomData, StringBuilder bericht) {
    geodataOrteNurInDateiNichtInDb.forEach(geodataOrtTemplate -> {
      Collection<GeodataPlz> geodataPlzTemplates = new HashSet<>(geodataOrtTemplate.getGeodataPostleitzahlen());

      geodataOrtTemplate.removeGeodataPostleitzahlen();
      geodataOrtTemplate.addGeodataPostleitzahlen(findOrCreate(geodataPlzTemplates));

      geodataOrtDao.persist(geodataOrtTemplate);
    });
  }

  @Override
  protected void aktualisiere(GeodataOrt geodataOrtInDatei, GeodataOrt geodataOrtInDb, NoContext context,
      DateiImportBerichtCustomData dateiImportBerichtCustomData, StringBuilder bericht) {
    geodataOrtInDb.setOrtsname(geodataOrtInDatei.getOrtsname());
    geodataOrtInDb.setOrtsteilname(geodataOrtInDatei.getOrtsteilname());
    geodataOrtInDb.setLatitude(geodataOrtInDatei.getLatitude());
    geodataOrtInDb.setLongitude(geodataOrtInDatei.getLongitude());
    geodataOrtInDb.addGeodataPostleitzahlen(findOrCreate(geodataOrtInDatei.getGeodataPostleitzahlen()));
  }

  @Override
  protected void loescheAusDatenbank(Collection<GeodataOrt> geodataOrteInDb, NoContext context,
      DateiImportBerichtCustomData dateiImportBerichtCustomData, StringBuilder bericht) {
    geodataOrteInDb.forEach(geodataOrt -> geodataOrtDao.remove(geodataOrt));
  }

  @Override
  protected List<GeodataOrt> getAllEntitiesInDB(NoContext noContext) {
    return geodataOrtDao.findByAll();
  }

  @Override
  protected Function<GeodataOrt, GeodataOrtUK> getEntityToEntityTKFunction() {
    return GeodataOrtUK.getGeodataOrtToUKFunction();
  }

  @Override
  protected void erstelleBericht(List<GeodataOrt> importObject, NoContext context, ImportOptions importOptions, StringBuilder bericht,
      boolean vorab) {
    // nichts zu tun
  }

  private Collection<GeodataPlz> findOrCreate(Collection<GeodataPlz> geodataPlzTemplates) {
    if (isNull(geodataPlzUkPerGeodataPlz)) {
      geodataPlzUkPerGeodataPlz = new HashMap<>(geodataPlzDao.findByAll().stream()
          .collect(Collectors.toMap(geodataPlz -> geodataPlz.getGeodataPlzUK(), geodataPlz -> geodataPlz)));
    }

    Collection<GeodataPlz> geodataPlzEntities = new HashSet<>();

    geodataPlzTemplates.forEach(geodataPlzTemplate -> {
      GeodataPlzUK geodataPlzUk = geodataPlzTemplate.getGeodataPlzUK();

      if (!geodataPlzUkPerGeodataPlz.containsKey(geodataPlzUk)) {
        geodataPlzDao.persist(geodataPlzTemplate);

        geodataPlzUkPerGeodataPlz.put(geodataPlzUk, geodataPlzTemplate);
      }

      geodataPlzEntities.add(geodataPlzUkPerGeodataPlz.get(geodataPlzUk));
    });

    return geodataPlzEntities;
  }
}
