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

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

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

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

import de.ivu.elect.business.datei.entity.DateiImportBerichtCustomData;
import de.ivu.elect.business.exchange.control.AbstractImporter;
import de.ivu.elect.business.exchange.control.ImportExchange;
import de.ivu.elect.business.exchange.entity.ImportUpdateReport;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsartDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsartEdit;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsarteigenschaftDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietsartCreate;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gebietsbaum.entity.GebietsartTK;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsarteigenschaft;
import de.ivu.elect.business.gebietsbaum.entity.GebietsarteigenschaftTK;
import de.ivu.elect.business.wahl.control.WahlKonfigurationImportExchange;
import de.ivu.elect.business.wahl.control.WahlKonfigurationMeta;
import de.ivu.elect.business.wahl.control.WahlartCreate;
import de.ivu.elect.business.wahl.entity.WahlKonfiguration;
import de.ivu.elect.business.wahl.entity.WahlKonfigurationExchangeContext;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.business.wahl.entity.WahlartTK;
import de.ivu.elect.jee.entity.Meta;
import de.ivu.elect.jee.exchange.ImportOptions;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.util.CollectionSeparator;

/**
 *  on 01.07.2015.
 */
@Stateless
public class WahlKonfigurationImporter extends AbstractImporter<WahlKonfiguration, WahlKonfigurationExchangeContext, DateiImportBerichtCustomData> {

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

  public static final String FILE_FORMAT_DEFAULT = "wahl.konfiguration.import.file.format";

  @Inject
  private transient WahlartDAO wahlartDAO;

  @Inject
  private transient GebietsartDAO gebietsartDAO;

  @Inject
  private transient WahlartCreate wahlartCreate;

  @Inject
  private transient GebietsartCreate gebietsartCreate;

  @Inject
  private transient GebietsartEdit gebietsartEdit;

  @Inject
  private transient WahlartEdit wahlartEdit;

  @Inject
  private transient GebietsarteigenschaftDAO gebietsarteigenschaftDAO;

  @Inject
  private transient WahlKonfigurationImportExchange importExchange;

  @Inject
  private transient WahlKonfigurationMeta wahlKonfigurationMeta;

  @Inject
  private transient GebietDAO gebietDAO;

  @Inject
  private transient WahlDAO wahlDAO;

  @Override
  protected void validiere(Violations violations, WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext noContext, ImportOptions importOptions) {
// nicht zu tun
  }

  @Override
  protected void erstelleBericht(WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext noContext, ImportOptions importOptions, StringBuilder bericht, boolean vorab) {
//nichts zu tun
  }

  @Override
  protected ImportUpdateReport createImportUpdateReport(WahlKonfiguration wahlKonfiguration, Violations violations, WahlKonfigurationExchangeContext noContext, ImportOptions importOptions, StringBuilder bericht) {
    return new ImportUpdateReport(violations, 0, 0, 0, importOptions, bericht);
  }

  @Override
  public void importiereImportObject(WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext context, ImportOptions importOptions,
                                DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
    // super.importiereImportObject(wahlKonfiguration, wahlKonfigurationExchangeContext, importOptions, customDateiImportBericht, bericht);
    Set<Gebietsart> gebietsartenInDB = importiereGebietsarten(context, wahlKonfiguration, customDateiImportBericht, importOptions);

    if (isWahlartenAnlegen()) {
      Set<Wahlart> wahlartenInDB = importiereWahlarten(context, wahlKonfiguration, customDateiImportBericht, importOptions);

      importiereGebietseigenschaften(context, wahlKonfiguration, gebietsartenInDB, wahlartenInDB, customDateiImportBericht);
    }
  }

  private void importiereGebietseigenschaften(WahlKonfigurationExchangeContext context, WahlKonfiguration wahlKonfiguration, Set<Gebietsart> gebietsartenInDB,
                                              Set<Wahlart> wahlartenInDB, DateiImportBerichtCustomData customDateiImportBericht) {
    Map<GebietsartTK, Gebietsart> gebietsartenInDBNachTK = GebietsartTK.mappeGebietsartNachTK(gebietsartenInDB);
    Map<WahlartTK, Wahlart> wahlartenInDBNachTK = WahlartTK.mappeWahlartNachTK(wahlartenInDB);

    CollectionSeparator<Gebietsarteigenschaft, GebietsarteigenschaftTK> gebietsarteigenschaften = new CollectionSeparator<>(
        gebietsarteigenschaftDAO.findByAll(context.getMandant()), wahlKonfiguration.getGebietsarteigenschaften(),
        Gebietsarteigenschaft::getGebietsarteigenschaftTK);

    // Neu anlegen
    for (Gebietsarteigenschaft gebietsarteigenschaft : gebietsarteigenschaften.getInSecondNotInFirst()) {
      gebietsarteigenschaft.setWahlart(wahlartenInDBNachTK.get(gebietsarteigenschaft.getWahlart().getWahlartTK()));
      gebietsarteigenschaft.setGebietsart(gebietsartenInDBNachTK.get(gebietsarteigenschaft.getGebietsart().getGebietsartTK()));
      if (gebietsarteigenschaft.getWahlartBeiAufteilung()!=null) {
        gebietsarteigenschaft.setWahlartBeiAufteilung(wahlartenInDBNachTK.get(gebietsarteigenschaft.getWahlartBeiAufteilung().getWahlartTK()));
      }
      gebietsarteigenschaftDAO.persist(gebietsarteigenschaft);
    }

    // aktualisieren
    for (GebietsarteigenschaftTK gebietsarteigenschaftTK : gebietsarteigenschaften.getInBoth()) {
      Gebietsarteigenschaft gebietsarteigenschaftInDB  = gebietsarteigenschaften.getFirstMap().get(gebietsarteigenschaftTK);
      Gebietsarteigenschaft gebietsarteigenschaftInDatei  = gebietsarteigenschaften.getSecondMap().get(gebietsarteigenschaftTK);
      gebietsarteigenschaftInDatei.copyEntityAttributesUsingSetter(gebietsarteigenschaftInDB);

      if (gebietsarteigenschaftInDatei.getWahlartBeiAufteilung() == null){
        gebietsarteigenschaftInDB.setWahlartBeiAufteilung(null);
      } else {
        gebietsarteigenschaftInDB.setWahlartBeiAufteilung(wahlartenInDBNachTK.get(gebietsarteigenschaftInDatei.getWahlartBeiAufteilung().getWahlartTK()));
      }
    }

    // Nicht löschen werden durch das Löschen der Gebietsarten und Wahlarten automatisch gelöscht

    customDateiImportBericht.setNurInDatei(customDateiImportBericht.getNurInDatei() + gebietsarteigenschaften.getInSecondNotInFirst().size());
    customDateiImportBericht.setNurInDatenbank(customDateiImportBericht.getNurInDatenbank() + gebietsarteigenschaften.getInFirstNotInSecond().size());
    customDateiImportBericht.setInDateiUndInDatenbank(customDateiImportBericht.getInDateiUndInDatenbank() + gebietsarteigenschaften.getInBoth().size());
  }

  private Set<Wahlart> importiereWahlarten(WahlKonfigurationExchangeContext context, WahlKonfiguration wahlKonfiguration,
                                           DateiImportBerichtCustomData customDateiImportBericht, ImportOptions importOptions) {

    List<Wahlart> urspruenglichInDb = wahlartDAO.findByAllOrderByName(context.getMandant());
    Set<Wahlart> wahlartenInDB = new HashSet<>(urspruenglichInDb);

    CollectionSeparator<Wahlart, WahlartTK> wahlarten = new CollectionSeparator<>(urspruenglichInDb, wahlKonfiguration.getWahlarten(), Wahlart::getWahlartTK);

    // Neu anlegen
    boolean isFlushNotwendig = false;
    for (Wahlart wahlart : wahlarten.getInSecondNotInFirst()) {
      wahlart.setMandant(context.getMandant());
      wahlartCreate.save(wahlart);
      wahlartenInDB.add(wahlart);
      isFlushNotwendig = true;
    }
    if (isFlushNotwendig) {
      wahlartDAO.flush(); // Damit in Finder verfügbar
    }
    // aktualisieren
    for (WahlartTK wahlartTK : wahlarten.getInBoth()) {
      Wahlart wahlartInDB = wahlarten.getFirstMap().get(wahlartTK);
      Wahlart wahlartInDatei = wahlarten.getSecondMap().get(wahlartTK);
      wahlartInDatei.copyEntityAttributesUsingSetter(wahlartInDB);
    }

    customDateiImportBericht.setNurInDatei(customDateiImportBericht.getNurInDatei() + wahlarten.getInSecondNotInFirst().size());
    customDateiImportBericht.setNurInDatenbank(customDateiImportBericht.getNurInDatenbank() + wahlarten.getInFirstNotInSecond().size());
    customDateiImportBericht.setInDateiUndInDatenbank(customDateiImportBericht.getInDateiUndInDatenbank() + wahlarten.getInBoth().size());

    // ELECTKOM-621 loeschen
    Collection<Wahlart> nichtMehrVerwendetInDb = wahlarten.getInFirstNotInSecond();
    if (!nichtMehrVerwendetInDb.isEmpty()) {
      LOG.info("Ueberzaehlige Wahlarten: " + nichtMehrVerwendetInDb);
      if (importOptions.isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()) {
        for (Wahlart wahlart : nichtMehrVerwendetInDb) {
          if (loescheWahlartWennNichtVerwendet(wahlart)) {
            wahlartenInDB.remove(wahlart);
          }
        }
      }
    }

    return wahlartenInDB;
  }

  private Set<Gebietsart> importiereGebietsarten(WahlKonfigurationExchangeContext context, WahlKonfiguration wahlKonfiguration,
                                                 DateiImportBerichtCustomData customDateiImportBericht, ImportOptions importOptions) {

    List<Gebietsart> urspruenglichInDb = gebietsartDAO.findByAll(context.getMandant());
    Set<Gebietsart> gebietsartenInDB = new HashSet<>(urspruenglichInDb);

    CollectionSeparator<Gebietsart, GebietsartTK> gebietsarten = new CollectionSeparator<>(urspruenglichInDb, wahlKonfiguration.getGebietsarten(),
        Gebietsart::getGebietsartTK);

    // Neu anlegen
    for (Gebietsart gebietsart : gebietsarten.getInSecondNotInFirst()) {
      gebietsart.setMandant(context.getMandant());
      gebietsartCreate.save(gebietsart);
      gebietsartenInDB.add(gebietsart);
    }

    // aktualisieren
    for (GebietsartTK gebietsartTK : gebietsarten.getInBoth()) {
      Gebietsart gebietsartInDB = gebietsarten.getFirstMap().get(gebietsartTK);
      Gebietsart gebietsartInDatei = gebietsarten.getSecondMap().get(gebietsartTK);
      gebietsartInDatei.copyEntityAttributesUsingSetter(gebietsartInDB);
    }

    customDateiImportBericht.setNurInDatei(customDateiImportBericht.getNurInDatei() + gebietsarten.getInSecondNotInFirst().size());
    customDateiImportBericht.setNurInDatenbank(customDateiImportBericht.getNurInDatenbank() + gebietsarten.getInFirstNotInSecond().size());
    customDateiImportBericht.setInDateiUndInDatenbank(customDateiImportBericht.getInDateiUndInDatenbank() + gebietsarten.getInBoth().size());

    // ELECTKOM-621 loeschen
    Collection<Gebietsart> nichtMehrVerwendetInDb = gebietsarten.getInFirstNotInSecond();
    if (!nichtMehrVerwendetInDb.isEmpty()) {
      LOG.info("Ueberzaehlige Gebietsarten: " + nichtMehrVerwendetInDb);
      if (importOptions.isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()) {
        for (Gebietsart gebietsart : nichtMehrVerwendetInDb) {
          if (loescheGebietsartWennNichtVerwendet(gebietsart)) {
            gebietsartenInDB.remove(gebietsart);
          }
        }
      }
    }

    return gebietsartenInDB;
  }

  protected boolean loescheGebietsartWennNichtVerwendet(Gebietsart gebietsart) {
    Long count = gebietDAO.findByCountGebieteByGebietsart(gebietsart);
    if (count == 0) {
      LOG.info("Loesche ueberzaehlige nicht mehr verwendete Gebietsart: " + gebietsart);
      gebietsartEdit.delete(gebietsart);
      gebietsartDAO.flush();
      return true;
    } else {
      LOG.warn("Ueberzaehlige Gebietsart: " + gebietsart + " wird in " + count + " Gebieten verwendet");
      return false;
    }
  }

  private boolean loescheWahlartWennNichtVerwendet(Wahlart wahlart) {
    Long count = wahlDAO.findByCountWahlart(wahlart);
    if (count == 0) {
      LOG.info("Loesche ueberzaehlige nicht mehr verwendete Wahlart: " + wahlart);
      wahlartEdit.delete(wahlart);
      wahlartDAO.flush();
      return true;
    } else {
      LOG.warn("Ueberzaehlige Wahlart: " + wahlart + " wird in " + count + " Wahlen verwendet");
      return false;
    }
  }

  @Override
  protected void anlegenWennNichtVorhanden(WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext context,
                                   DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
    throw new UnsupportedOperationException(); // importiereImportObject
  }

  @Override
  protected void aktualisiereBestehendenDaten(WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext noContext, DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
    throw new UnsupportedOperationException(); // importiereImportObject
  }

  @Override
  protected void loescheBestehendenDatenWennNichtInImportDatenVorhanden(WahlKonfiguration wahlKonfiguration, WahlKonfigurationExchangeContext noContext, DateiImportBerichtCustomData customDateiImportBericht, StringBuilder bericht) {
    throw new UnsupportedOperationException(); // importiereImportObject
  }

  @Override
  protected ImportExchange<WahlKonfiguration, WahlKonfigurationExchangeContext> getExchange() {
    return importExchange;
  }

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

  protected boolean isWahlartenAnlegen() {
    return true;
  }

  @Override
  protected boolean isUnterstuetztAnlegenWennNichtVorhanden() {
    return true;
  }

  @Override
  protected boolean isUnterstuetztAktualisiereBestehendenDaten() {
    return true;
  }

  @Override
  protected boolean isUnterstuetztLoescheBestehendenDatenWennNichtInImportDatenVorhanden() {
    return true;
  }

}
