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

import java.util.HashSet;
import java.util.Set;

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

import org.apache.commons.lang3.StringUtils;
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.GebietsartDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsartEdit;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsarteigenschaftDAO;
import de.ivu.elect.business.gebietsbaum.boundary.GebietsarteigenschaftEdit;
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.mandant.entity.Mandant;
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 GebietsarteigenschaftEdit gebietsarteigenschaftEdit;

  @Inject
  private transient WahlartEdit wahlartEdit;

  @Inject
  private transient GebietsarteigenschaftDAO gebietsarteigenschaftDAO;

  @Inject
  private transient WahlKonfigurationImportExchange importExchange;

  @Inject
  private transient WahlKonfigurationMeta wahlKonfigurationMeta;

  @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);

    var verwendeteGebietsarten = bestimmeVerwendeteGebietsarten(context.getMandant());
    LOG.info("Verwendete Gebietsarten: " + verwendeteGebietsarten);

    var gebietsartenInDB = importiereGebietsarten(context, wahlKonfiguration, customDateiImportBericht, importOptions, verwendeteGebietsarten);

    if (isWahlartenAnlegen()) {
      var verwendeteWahlarten = bestimmeVerwendeteWahlarten(context.getMandant());
      LOG.info("Verwendete Wahlarten: " + verwendeteWahlarten);

      var verwendeteGebietsarteigenschaften = bestimmeVerwendeteGebietsarteigenschaften(context.getMandant());
      LOG.info("Verwendete Gebietsarteigenschaften: " + verwendeteGebietsarteigenschaften);

      Set<Wahlart> ueberzaehligeWahlarten = new HashSet<>();
      var wahlartenInDB = importiereWahlarten(context, wahlKonfiguration, customDateiImportBericht, importOptions, verwendeteWahlarten,
          ueberzaehligeWahlarten);

      importiereGebietseigenschaften(context, wahlKonfiguration, gebietsartenInDB, wahlartenInDB, customDateiImportBericht, importOptions,
          ueberzaehligeWahlarten, verwendeteGebietsarteigenschaften);
    }
  }

  private Set<Gebietsarteigenschaft> bestimmeVerwendeteGebietsarteigenschaften(Mandant mandant) {
    return new HashSet<>(gebietsarteigenschaftDAO.findByInMandantVerwendet(mandant));
  }

  private Set<Wahlart> bestimmeVerwendeteWahlarten(Mandant mandant) {
    return new HashSet<>(wahlartDAO.findByInMandantVerwendet(mandant));
  }

  private Set<Gebietsart> bestimmeVerwendeteGebietsarten(Mandant mandant) {
    return new HashSet<>(gebietsartDAO.findByInMandantVerwendet(mandant));
  }

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

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

    // Neu anlegen
    LOG.info("Gebietsarteigenschaften anlegen: " + gebietsarteigenschaften.getInSecondNotInFirst().size());
    for (var 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
    LOG.info("Gebietsarteigenschaften aktualisieren: " + gebietsarteigenschaften.getInBoth().size());
    for (var gebietsarteigenschaftTK : gebietsarteigenschaften.getInBoth()) {
      var gebietsarteigenschaftInDB = gebietsarteigenschaften.getFirstMap().get(gebietsarteigenschaftTK);
      var gebietsarteigenschaftInDatei = gebietsarteigenschaften.getSecondMap().get(gebietsarteigenschaftTK);
      gebietsarteigenschaftInDatei.copyEntityAttributesUsingSetter(gebietsarteigenschaftInDB);

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

    // ELECT-12107 ELECT-14570 Nicht löschen werden durch das Löschen der Gebietsarten und Wahlarten automatisch gelöscht
    if (importOptions.isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()) {
      LOG.info("Gebietsarteigenschaften löschen: " + gebietsarteigenschaften.getInFirstNotInSecond().size());
      for (var gebietsarteigenschaft : gebietsarteigenschaften.getInFirstNotInSecond()) {
        loescheGebietsarteigenschaftWennNichtVerwendet(gebietsarteigenschaft, ueberzaehligeWahlarten, verwendeteGebietsarteigenschaften, wahlartenInDB,
            gebietsartenInDB);
      }
    }

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

  private void loescheGebietsarteigenschaftWennNichtVerwendet(Gebietsarteigenschaft gebietsarteigenschaft, Set<Wahlart> ueberzaehligeWahlarten,
                                                              Set<Gebietsarteigenschaft> verwendeteGebietsarteigenschaften, Set<Wahlart> wahlartenInDB, Set<Gebietsart> gebietsartenInDB) {

    // ELECT-12477
    if (!wahlartenInDB.contains(gebietsarteigenschaft.getWahlart()) || !gebietsartenInDB.contains(gebietsarteigenschaft.getGebietsart())) {
      LOG.info("Ueberzaehlige Gebietsarteigenschaft wird kaskadiert gelöscht: " + gebietsarteigenschaft);
      return;
    }

    if (verwendeteGebietsarteigenschaften.contains(gebietsarteigenschaft) || ueberzaehligeWahlarten.contains(gebietsarteigenschaft.getWahlart())) {
      LOG.warn("Ueberzaehlige Gebietsarteigenschaft: " + gebietsarteigenschaft + " wird in Wahlen verwendet");
    } else {
      LOG.info("Loesche ueberzaehlige nicht mehr verwendete Gebietsarteigenschaft: " + gebietsarteigenschaft);
      gebietsarteigenschaftEdit.delete(gebietsarteigenschaft);
      gebietsarteigenschaftDAO.flush();
    }
  }

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

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

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

    // Neu anlegen
    var isFlushNotwendig = false;
    LOG.info("Wahlarten anlegen: " + wahlarten.getInSecondNotInFirst().size());
    for (var 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
    LOG.info("Wahlarten aktualisieren: " + wahlarten.getInBoth().size());
    for (var wahlartTK : wahlarten.getInBoth()) {
      var wahlartInDB = wahlarten.getFirstMap().get(wahlartTK);
      var 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
    LOG.info("Wahlarten loeschen: " + wahlarten.getInFirstNotInSecond().size());
    var nichtMehrVerwendetInDb = wahlarten.getInFirstNotInSecond();
    if (!nichtMehrVerwendetInDb.isEmpty()) {
      LOG.info("Ueberzaehlige Wahlarten: " + nichtMehrVerwendetInDb);
      if (importOptions.isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()) {
        for (var wahlart : nichtMehrVerwendetInDb) {
          if (loescheWahlartWennNichtVerwendet(wahlart, verwendeteWahlarten)) {
            wahlartenInDB.remove(wahlart);
          } else {
            ueberzaehligeWahlarten.add(wahlart);
          }
        }
      }
    }

    return wahlartenInDB;
  }

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

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

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

    LOG.info("Gebietsarten anlegen: " + gebietsarten.getInSecondNotInFirst().size());
    // Neu anlegen
    for (var gebietsart : gebietsarten.getInSecondNotInFirst()) {
      gebietsart.setMandant(context.getMandant());
      gebietsartCreate.save(gebietsart);
      gebietsartenInDB.add(gebietsart);
    }

    // aktualisieren
    LOG.info("Gebietsarten aktualisieren: " + gebietsarten.getInBoth().size());
    for (var gebietsartTK : gebietsarten.getInBoth()) {
      var gebietsartInDB = gebietsarten.getFirstMap().get(gebietsartTK);
      var 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
    LOG.info("Gebietsarten nichtMehrVerwendetInDb: " + gebietsarten.getInFirstNotInSecond().size());
    var nichtMehrVerwendetInDb = gebietsarten.getInFirstNotInSecond();
    if (!nichtMehrVerwendetInDb.isEmpty()) {
      LOG.info("Ueberzaehlige Gebietsarten: " + nichtMehrVerwendetInDb);
      if (importOptions.isLoescheBestehendenDatenWennNichtInImportDatenVorhanden()) {
        for (var gebietsart : nichtMehrVerwendetInDb) {
          if (loescheGebietsartWennNichtVerwendet(gebietsart, verwendeteGebietsarten)) {
            gebietsartenInDB.remove(gebietsart);
          }
        }
      }
    }

    return gebietsartenInDB;
  }

  protected boolean loescheGebietsartWennNichtVerwendet(Gebietsart gebietsart, Set<Gebietsart> verwendeteGebietsarten) {
    if (verwendeteGebietsarten.contains(gebietsart)) {
      markiereLegacy(gebietsart);
      return false;
    }

    LOG.info("Loesche ueberzaehlige nicht mehr verwendete Gebietsart: " + gebietsart);
    gebietsartEdit.delete(gebietsart);
    gebietsartDAO.flush();
    return true;

  }

  private boolean loescheWahlartWennNichtVerwendet(Wahlart wahlart, Set<Wahlart> verwendeteWahlarten) {
    if (verwendeteWahlarten.contains(wahlart)) {
      markiereLegacy(wahlart);
      return false;
    }

    LOG.info("Loesche ueberzaehlige nicht mehr verwendete Wahlart: " + wahlart);
    wahlartEdit.delete(wahlart);
    wahlartDAO.flush();
    return true;
  }

  protected void markiereLegacy(Wahlart wahlart) {
    // ELECT-15686
    LOG.warn("Ueberzaehlige Wahlart: " + wahlart + " wird in Wahlen verwendet");

    var veraltet = getMessages().getMessageForMessageKey("wahl.konfiguration.import.legacy.wahlart");
    if (StringUtils.isNotBlank(veraltet) && !isEnthalten(wahlart.getTitel(), veraltet)) {
      if (wahlart.getTitel() == null) {
        wahlart.setTitel(veraltet);
      } else {
        wahlart.setTitel(wahlart.getTitel() + " " + veraltet);
      }
    }
  }

  protected void markiereLegacy(Gebietsart gebietsart) {
    // ELECT-15686
    LOG.warn("Ueberzaehlige Gebietsart: " + gebietsart + " wird in Gebieten verwendet");
    var veraltet = getMessages().getMessageForMessageKey("wahl.konfiguration.import.legacy.gebietsart");
    if (StringUtils.isNotBlank(veraltet) && !isEnthalten(gebietsart.getTitel(), veraltet)) {
      if (gebietsart.getTitel() == null) {
        gebietsart.setTitel(veraltet);
      } else {
        gebietsart.setTitel(gebietsart.getTitel() + " " + veraltet);
      }
    }
  }

  private boolean isEnthalten(String titel, String veraltet) {
    if (StringUtils.isBlank(titel)) {
      return false;
    }
    return titel.contains(veraltet);
  }

  @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;
  }

}
