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

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietTK;
import de.ivu.elect.business.gruppe.control.GruppeExchangeContext;
import de.ivu.elect.business.gruppe.control.GruppeImportExchange;
import de.ivu.elect.business.gruppe.entity.AbstractGruppe;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppe.entity.GruppeMeta;
import de.ivu.elect.business.gruppe.entity.GruppeTK;
import de.ivu.elect.business.gruppeimgebiet.control.EinzelbewerberTrittDoppeltAnViolation;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietCreate;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebietTK;
import de.ivu.elect.business.image.boundary.BildDAO;
import de.ivu.elect.business.image.entity.Bild;
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.entity.customdata.EmptyCustomData;
import de.ivu.elect.jee.exchange.ImportOptions;
import de.ivu.elect.jee.exchange.Violations;

@Stateless
public class GruppeImporter
    extends
      AbstractEntityImporter<Gruppe, GruppeTK, GruppeExchangeContext, DateiImportBerichtCustomData> {

  @Inject
  transient WahlDAO wahlDAO;

  @Inject
  private transient GruppeMeta gruppeMeta;

  @Inject
  transient GruppeDAO gruppeDAO;

  @Inject
  private transient GruppeEdit gruppeEdit;

  @Inject
  private transient GruppeImGebietCreate gruppeImGebietCreate;

  @Inject
  private transient GruppeImportExchange gruppeImportExchange;

  @Inject
  transient GebietDAO gebietDAO;

  @Inject
  private transient BildDAO bildDAO;

  @Override
  protected void persistiere(Collection<Gruppe> neueEntities, GruppeExchangeContext context,
      DateiImportBerichtCustomData customDateiImportBerichtCustomData, StringBuilder bericht) {
    Wahl wahl = wahlDAO.attach(context.getWahl());

    for (Gruppe gruppe : neueEntities) {
      gruppe.setWahl(wahl);
      if (gruppe.getLogo()!=null){
        bildDAO.persist(gruppe.getLogo());
      }
      // ELECTSL-34
      if (gruppe.getFarbe()==null){
        gruppe.setFarbe(new Gruppe(EmptyCustomData.INSTANCE).getFarbe());
      }

      gruppeDAO.persist(gruppe);

      speichereGebietspezifischeNamen(wahl, gruppe);
    }
  }

  protected void speichereGebietspezifischeNamen(Wahl wahl, Gruppe gruppe) {
    // Gebietspezifische Namen speichern
    if (!gruppe.getGruppenImGebiet().isEmpty()) {

      List<Gebiet> gebieteDerWahl = gebietDAO.findByWahl(wahl);

      if (gebieteDerWahl.isEmpty()) {
        throw new IllegalStateException("Keine Gebiete vorhanden");
      }
      Gebiet wurzelgebiet = gebieteDerWahl.get(0).getWurzelgebiet();

      Map<GebietTK, Gebiet> vorhandeneGebieteNachTKMap = GebietTK.mappeGebietNachTK(gebieteDerWahl);
      for (GruppeImGebiet gruppeImGebiet : gruppe.getGruppenImGebiet()) {
        Gebiet gebietInDatei = gruppeImGebiet.getGebiet();
        Gebiet passendesGebietInDb = vorhandeneGebieteNachTKMap.get(gebietInDatei.getGebietTK());

        if (passendesGebietInDb == null) {
          throw new IllegalStateException("Gebiet nicht gefunden: " + gebietInDatei.getGebietTK());
        }

        // Wenn oberstes Gebiet, dann steht der Name bereits in der Gruppe selbst, die GruppeImGebiet muss für diesen Fall nicht angelegt werden
        if (!passendesGebietInDb.equals(wurzelgebiet)) {
          gruppeImGebiet.setGebiet(passendesGebietInDb);
          gruppeImGebietCreate.save(gruppeImGebiet);
        }
      }
    }
  }

  @Override
  protected void aktualisiere(Gruppe entityInDatei, Gruppe entityInDatenbank, GruppeExchangeContext context,
      DateiImportBerichtCustomData customDateiImportBerichtCustomData, StringBuilder bericht) {

    entityInDatenbank.setFarbe(entityInDatei.getFarbe());
    entityInDatenbank.setFarbeExtern(entityInDatei.getFarbeExtern());
    entityInDatenbank.setKurzname(entityInDatei.getKurzname());
    entityInDatenbank.setName(entityInDatei.getName());
    entityInDatenbank.setInPraesentation(entityInDatei.getInPraesentation());
    entityInDatenbank.setPrivilegiert(entityInDatei.getPrivilegiert());

    aktualisiereCustomData(entityInDatei, entityInDatenbank);

    aktualisiereGebietspezifischeNamen(context.getWahl(), entityInDatei, entityInDatenbank);

    if (entityInDatei.getLogo()!=null){
      if (entityInDatenbank.getLogo()==null){
        Bild logo = new Bild();
        bildDAO.persist(logo);
        entityInDatenbank.setLogo(logo);
      }
      entityInDatenbank.getLogo().setContentBase64(entityInDatei.getLogo().getContentBase64());
      entityInDatenbank.getLogo().setMimeType(entityInDatei.getLogo().getMimeType());
    }

    if (entityInDatei.getRelativePosition() != null) {
      entityInDatenbank.setRelativePosition(entityInDatei.getRelativePosition());
      entityInDatenbank.setVorgegebenePosition(entityInDatei.getVorgegebenePosition());
    }

  }

  protected void aktualisiereCustomData(Gruppe entityInDatei, Gruppe entityInDatenbank) {
    // nichts zu tun hier
  }

  protected void aktualisiereGebietspezifischeNamen(Wahl wahl, Gruppe gruppeInDatei, Gruppe gruppeInDb) {
    if (!gruppeInDatei.getGruppenImGebiet().isEmpty()) {

      List<Gebiet> gebieteDerWahl = gebietDAO.findByWahl(wahl);

      if (gebieteDerWahl.isEmpty()) {
        throw new IllegalStateException("Keine Gebiete vorhanden");
      }
      Gebiet wurzelgebiet = gebieteDerWahl.get(0).getWurzelgebiet();

      Map<GebietTK, Gebiet> vorhandeneGebieteNachTKMap = GebietTK.mappeGebietNachTK(gebieteDerWahl);
      Map<GruppeImGebietTK, GruppeImGebiet> vorhandeneGruppeImGebietNachTKMap = GruppeImGebietTK
          .mappeGruppeImGebietNachTK(gruppeInDb.getGruppenImGebiet());
      for (GruppeImGebiet gruppeImGebiet : gruppeInDatei.getGruppenImGebiet()) {
        Gebiet gebietInDatei = gruppeImGebiet.getGebiet();
        Gebiet passendesGebietInDb = vorhandeneGebieteNachTKMap.get(gebietInDatei.getGebietTK());

        if (passendesGebietInDb == null) {
          throw new IllegalStateException("Gebiet nicht gefunden: " + gebietInDatei.getGebietTK());
        }

        GruppeImGebiet passendeGruppeImGebietInDb = vorhandeneGruppeImGebietNachTKMap.get(gruppeImGebiet
            .getGruppeImGebietTK());

        if (passendeGruppeImGebietInDb == null) {
          // Anlegen...
          // Wenn oberstes Gebiet, dann steht der Name bereits in der Gruppe selbst, die GruppeImGebiet muss für diesen Fall nicht angelegt werden
          if (!passendesGebietInDb.equals(wurzelgebiet)) {
            gruppeImGebiet.setGebiet(passendesGebietInDb);
            gruppeImGebiet.setGruppe(gruppeInDb);
            gruppeImGebietCreate.save(gruppeImGebiet);
          }

        } else {
          // Aktualisieren...
          passendeGruppeImGebietInDb.setName(gruppeImGebiet.getName());
          passendeGruppeImGebietInDb.setKurzname(gruppeImGebiet.getKurzname());
          aktualisiereCustomData(gruppeImGebiet, passendeGruppeImGebietInDb);

          vorhandeneGruppeImGebietNachTKMap.remove(gruppeImGebiet.getGruppeImGebietTK());
        }
      }

      // Nicht aktualisierte GruppeImGebiet zurücksetzen
      for (GruppeImGebiet gruppeImGebiet : vorhandeneGruppeImGebietNachTKMap.values()) {
        gruppeImGebiet.setName(null);
        gruppeImGebiet.setKurzname(null);
        gruppeImGebiet.setZusatzbezeichnung(null);
        aktualisiereCustomData(null, gruppeImGebiet);
      }
    }
  }

  protected void aktualisiereCustomData(GruppeImGebiet entityInDatei, GruppeImGebiet entityInDatenbank) {
    // nichts zu tun hier
  }

  @Override
  protected void loescheAusDatenbank(Collection<Gruppe> entitiesInDb, GruppeExchangeContext context,
      DateiImportBerichtCustomData customDateiImportBerichtCustomData, StringBuilder bericht) {
    for (Gruppe gruppe : entitiesInDb) {
      gruppeEdit.delete(gruppe);
    }
  }

  @Override
  protected List<Gruppe> getAllEntitiesInDB(GruppeExchangeContext context) {
    return gruppeDAO.findByWahl(wahlDAO.attach(context.getWahl()));
  }

  @Override
  protected Function<Gruppe, GruppeTK> getEntityToEntityTKFunction() {
    return GruppeTK.getGruppeToTKFunction();
  }

  @Override
  protected void validiere(Violations violations, List<Gruppe> importObject, GruppeExchangeContext context, ImportOptions importOptions) {
    Wahl wahl = wahlDAO.attach(context.getWahl());

    AbstractGruppe.relativePositionSetzenWennNoetig(importObject, gruppeDAO.findByWahl(wahl));

    List<Gebiet> gebieteDerWahl = gebietDAO.findByWahl(wahl);
    Map<GebietTK, Gebiet> vorhandeneGebieteNachTKMap = GebietTK.mappeGebietNachTK(gebieteDerWahl);

    Set<GebietTK> fehlendeGebiete = new HashSet<>();

    for (Gruppe gruppe : importObject) {
      validateEntity(violations, gruppe, gruppe.getGruppeTK());

      if (gruppe.isEinzelbewerber() && gruppe.getGruppenImGebiet().size() > 1) {
        violations.add(new EinzelbewerberTrittDoppeltAnViolation(gruppe.getGruppeTK()));
      }

      for (GruppeImGebiet gruppeImGebiet : gruppe.getGruppenImGebiet()) {

        GebietTK gebietTK = gruppeImGebiet.getGebiet().getGebietTK();
        if (!vorhandeneGebieteNachTKMap.containsKey(gebietTK)) {
          fehlendeGebiete.add(gebietTK);
        }
      }
    }

    for (GebietTK gebietTK : fehlendeGebiete) {
      violations.addError("gruppe.import.gebiet.fehlt", gebietTK.getNummer(), gebietTK
          .getGebietsartTK().getName());
    }
  }

  @Override
  protected void erstelleBericht(List<Gruppe> importObject, GruppeExchangeContext context, ImportOptions importOptions,
      StringBuilder bericht, boolean vorab) {
    Map<GruppeArt, Integer> anzahlProGruppeArt = new LinkedHashMap<>();

    for (GruppeArt art : GruppeArt.values()) {
      anzahlProGruppeArt.put(art, 0);
    }

    for (Gruppe gruppe : importObject) {
      GruppeArt art = gruppe.getGruppeArt();
      if (art!=null) {
        anzahlProGruppeArt.put(art, anzahlProGruppeArt.get(art) + 1);
      }
    }

    bericht.append(getMessage("gruppe.import.gruppe.art.anzahl")).append('\n');

    bericht.append(anzahlProGruppeArt.entrySet().stream().map(entry -> getMessage("GruppeArt." + entry.getKey().name()) + ": " + entry.getValue() + '\n')
        .collect(Collectors.joining()));
  }

  @Override
  protected ImportExchange<List<Gruppe>, GruppeExchangeContext> getExchange() {
    return gruppeImportExchange;
  }

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