
package de.ivu.elect.was.exchange.wahl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.inject.Inject;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.EmlImportMapperHelper;
import de.ivu.elect.EmlValidation;
import de.ivu.elect.business.aufgabe.entity.Aufgabe;
import de.ivu.elect.business.datei.entity.Datei;
import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.fremdkandidatur.entity.Fremdkandidatur;
import de.ivu.elect.business.gebietsbaum.boundary.GebietDAO;
import de.ivu.elect.business.gebietsbaum.control.EntityMitWurzelgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPolygonPoint;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsarteigenschaft;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsgruppierung;
import de.ivu.elect.business.gebietsbaum.entity.NlGebietsart;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppeEigenschaften;
import de.ivu.elect.business.gruppeimgebiet.control.GruppeImGebietReihenfolgeVorsortierer;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.internet.entity.InternetSeite;
import de.ivu.elect.business.kandidatenportal.entity.WvpWarnung;
import de.ivu.elect.business.kandidatur.control.StimmzettelFormatter;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.person.entity.PersonenVerknuepfung;
import de.ivu.elect.business.personenpruefung.entity.PersonPruefungKonfiguration;
import de.ivu.elect.business.pruefung.boundary.ZulassungPrueferDAOFactory;
import de.ivu.elect.business.staat.entity.StaatenGruppe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.business.stimmzettel.boundary.EntityAufStimmzettelDatenStimmzettelPositionBerechnung;
import de.ivu.elect.business.stimmzettel.entity.Stimmzettel;
import de.ivu.elect.business.stimmzettel.entity.StimmzettelKopfdatum;
import de.ivu.elect.business.veranstaltung.entity.Veranstaltung;
import de.ivu.elect.business.wahl.boundary.NlWahlImportHelper;
import de.ivu.elect.business.wahl.boundary.WahlDAO;
import de.ivu.elect.business.wahl.control.GewaehlteLoader;
import de.ivu.elect.business.wahl.control.Loader;
import de.ivu.elect.business.wahl.control.NlWahlParameterPostCreate;
import de.ivu.elect.business.wahl.control.SitzeLoader;
import de.ivu.elect.business.wahl.control.StimmenLoader;
import de.ivu.elect.business.wahl.control.WahlImportObject;
import de.ivu.elect.business.wahl.control.Wahldaten;
import de.ivu.elect.business.wahl.entity.NlWahlCustomData;
import de.ivu.elect.business.wahl.entity.NlWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlImportContext;
import de.ivu.elect.business.wahl.entity.WahlParameter;
import de.ivu.elect.business.wahl.entity.WahlParameterLoader;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.business.wahlausschuss.entity.WahlausschussEntschaedigung;
import de.ivu.elect.business.wahlausschuss.entity.Wahlausschussmitglied;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungVerlauf;
import de.ivu.elect.business.wahlrechtspruefung.entity.Pruefkriterium;
import de.ivu.elect.business.wahlrechtspruefung.entity.Wahlrechtspruefung;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlagsperson.entity.WahlvorschlagsPerson;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaStringSelectOneProperty;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyStringProperty;
import de.ivu.elect.nl.eml.EmlValidator;
import de.ivu.elect.nl.eml.EmlWrapper;
import de.ivu.elect.nl.eml.readable.Eml110aWrapper;
import de.ivu.elect.presentation.NlSystemProperties;
import de.ivu.wahl.wus.oasis.eml.kiesraad.ElectionSubcategoryType;
import de.ivu.wahl.wus.oasis.eml.kiesraad.Region;
import de.ivu.wahl.wus.oasis.eml.kiesraad.RegionCategoryType;


import static de.ivu.elect.business.wahl.boundary.NlWasWahlImportHelper.CONTEXT_PARAM_ALS_WAHLDEFINITION_IMPORTIEREN;
import static de.ivu.elect.business.wahl.boundary.NlWasWahlImportHelper.CONTEXT_PARAM_WURZELGEBIET;
import static java.util.Collections.emptyList;

public abstract class AbstractEml110WahlImportMapper<Wahlobject extends Wahl, Gebietobject extends Gebiet> implements WahlImportMapper, Serializable {

  private static final long serialVersionUID = 1L;

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

  public static final String EML_ID = Eml110aZipWahlExportMapper.EML_ID;

  public static final String FILE_FORMAT_NAME = Eml110aZipWahlExportMapper.FILE_FORMAT_NAME;

  public static final FileFormat FILE_FORMAT = new FileFormat(FILE_FORMAT_NAME, FileType.XML);

  public static final String EML_110_WURZELGEBIET_LABEL = "eml110.wurzelgebiet.label";

  @Inject
  protected transient Messages messages;

  @Inject
  protected transient SystemGruppeEigenschaften systemGruppeEigenschaften;

  @Inject
  protected transient ZulassungPrueferDAOFactory zulassungPrueferDAOFactory;

  @Inject
  protected transient StimmzettelFormatter stimmzettelFormatter;

  @Inject
  protected transient EntityAufStimmzettelDatenStimmzettelPositionBerechnung entityAufStimmzettelDatenStimmzettelPositionBerechnung;

  @Inject
  protected transient GruppeImGebietReihenfolgeVorsortierer gruppeImGebietReihenfolgeVorsortierer;

  @Inject
  protected transient Eml110Mapper eml110Mapper;

  @Inject
  protected transient NlWahlImportHelper nlWahlImportHelper;

  @Inject
  protected transient EmlImportMapperHelper emlImportMapperHelper;

  @Inject
  protected transient NlSystemProperties nlSystemProperties;

  @Inject
  protected transient WahlDAO wahlDAO;

  @Inject
  protected transient GebietDAO gebietDAO;

  @Inject
  protected transient WahlParameterLoader wahlParameterLoader;

  @Inject
  protected transient NlWahlParameterPostCreate nlWahlParameterPostCreate;

  protected AbstractEml110WahlImportMapper() {
    super();
  }

  @Override
  public boolean isAcceptContext(WahlImportContext context) {
    return true;
  }

  @Override
  public FileFormat getFileFormat() {
    return FILE_FORMAT;
  }

  protected String getEmlId() {
    return EML_ID;
  }

  @Override
  public WahlImportObject deserialize(InputStream is, WahlImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    var emlWrapper = emlImportMapperHelper.readEmlNoHash(is);

    var wahl = (Wahlobject) eml110Mapper.mappeWahl(emlWrapper);

    wahl.getCustomData(NlWahlCustomData.class).setEml110Filename(context.get("Eml110Filename", String.class));//ELECT-10358
    wahl.getCustomData(NlWahlCustomData.class).setEml110Date(emlWrapper.getCreationDate());
    wahl.getCustomData(NlWahlCustomData.class).setEml110HashCode(context.get(EmlImportMapperHelper.EMLHASHCODE, String.class));

    var wahlart = wahl.getWahlart();
    // ELECT-9760 "PS" Super-Duper-Spezial-Wahlartmapping
    adjustPSWahlart(wahl, wahlart, emlWrapper);

    List<WahlParameter> wahlparameter = new ArrayList<>();

    var gebiete = mappeGebiete(context, emlWrapper, wahl, wahlparameter);
    var gruppen = mappeGruppen(context, emlWrapper, wahl, wahlparameter);
    var gruppenImGebiet = mappeGruppenImGebiet(gebiete, gruppen);

    postWahlMapping(wahl, emlWrapper);

    Loader<Gebiet> gebieteLoader = () -> gebiete;
    Loader<Gruppe> gruppenLoader = () -> gruppen;
    Loader<GruppeImGebiet> gruppenImGebietLoader = () -> gruppenImGebiet;
    Loader<Wahlvorschlag> wahlvorschlaegeLoader = Collections::emptyList;
    Loader<Kandidatur> kandidaturenLoader = Collections::emptyList;
    Loader<PersonenVerknuepfung> personenVerknuepfungenLoader = Collections::emptyList;
    StimmenLoader stimmenLoader = ergebnisart -> emptyList();
    GewaehlteLoader gewaehlteLoader = ignored -> Collections.emptyList();
    SitzeLoader sitzeLoader = ignored -> Collections.emptyList();
    Loader<WahlvorschlagsPerson> wahlvorschlagsPersonenLoader = Collections::emptyList;
    Loader<Wahlausschussmitglied> wahlausschussmitgliederLoader = Collections::emptyList;
    Loader<WahlausschussEntschaedigung> wahlausschussEntschaedigungenLoader = Collections::emptyList;
    Loader<StaatenGruppe> staatenGruppenLoader = Collections::emptyList;
    Loader<Gebietsgruppierung> gebietsgruppierungenLoader = Collections::emptyList;
    Loader<GebietPolygonPoint> gebietPolygonPointsLoader = Collections::emptyList;
    Loader<Fremdkandidatur> fremdkandidaturenLoader = Collections::emptyList;
    Loader<InternetSeite> internetSeitenLoader = Collections::emptyList;
    Loader<WahlbeteiligungVerlauf> wahlbeteiligungVerlaeufeLoader = Collections::emptyList;
    Loader<StimmeingabePlausiKonfiguration> stimmeingabePlausiKonfigurationLoader = Collections::emptyList;
    Loader<Veranstaltung> veranstaltungenLoader = Collections::emptyList;
    Loader<Stimmzettel> stimmzettelLoader = Collections::emptyList;
    Loader<StimmzettelKopfdatum> stimmzettelKopfdatenLoader = Collections::emptyList;
    Loader<Pruefkriterium> pruefkriterienLoader = Collections::emptyList;
    Loader<Wahlrechtspruefung> wahlrechtspruefungenLoader = Collections::emptyList;
    Loader<WvpWarnung> wvpWarnungenLoader = Collections::emptyList;
    Loader<PersonPruefungKonfiguration> personPruefungKonfigurationLoader = Collections::emptyList;
    Loader<Aufgabe> aufgabeLoader = Collections::emptyList;

    try { // ELECT-13993 (andere Stellen beachten(!))
      var wurzelgebiet = gebiete.stream().filter(g -> g.getUebergeordnetesGebiet() == null).findAny().orElse(null);

      nlWahlParameterPostCreate.overrideDokumentPlatzhalter(wahl, wurzelgebiet, wahlParameterWerte -> createWahlparameter(wahl, wahlParameterWerte))
          .forEach(wahlparameter::add);
    } catch (Exception ex) {
      LOG.warn("Fehler bei Sonderbehandlung Dokumentplatzhalter" + ex.getMessage(), ex);
    }

    var wahldaten = new Wahldaten<Wahlobject, Gebiet>(wahl, gebieteLoader, gruppenLoader, gruppenImGebietLoader, wahlvorschlaegeLoader,
        kandidaturenLoader, personenVerknuepfungenLoader,
        stimmenLoader, gewaehlteLoader, sitzeLoader, wahlvorschlagsPersonenLoader, wahlausschussmitgliederLoader,
        wahlausschussEntschaedigungenLoader, staatenGruppenLoader,
        gebietsgruppierungenLoader, systemGruppeEigenschaften, wahlParameterLoader, zulassungPrueferDAOFactory.createZulassungPruefer(), stimmzettelFormatter,
        entityAufStimmzettelDatenStimmzettelPositionBerechnung, gruppeImGebietReihenfolgeVorsortierer, gebietPolygonPointsLoader, fremdkandidaturenLoader,
        internetSeitenLoader,
        wahlbeteiligungVerlaeufeLoader, stimmeingabePlausiKonfigurationLoader, veranstaltungenLoader, stimmzettelLoader, stimmzettelKopfdatenLoader,
        pruefkriterienLoader, wahlrechtspruefungenLoader,
        wvpWarnungenLoader, personPruefungKonfigurationLoader, aufgabeLoader);

    return new WahlImportObject(wahldaten, false, emptyList(), wahlparameter);
  }

  private WahlParameter createWahlparameter(Wahlobject wahl, WahlParameterWerte wahlParameterWerte) {
    var result = new WahlParameter();
    result.setWahl(wahl);
    result.setWahltermin(wahl.getWahltermin());
    result.setKey(wahlParameterWerte.getKey());

    return result;
  }

  private void adjustPSWahlart(Wahl wahl, Wahlart wahlart, EmlWrapper emlWrapper) {
    if (NlWahlart.PROVINCIALE_STATEN.equals(NlWahlart.valueOf(wahlart))) {
      LOG.info("Provinziale Staaten");

      var electionSubcategoryType = versucheSubCategorieZuLesen(emlWrapper);

      NlWahlart nlWahlartProvincialeStaten;

      // ELECT-13590 zuerst die SubCategorie betrachten
      if (ElectionSubcategoryType.PS_1.equals(electionSubcategoryType)) {
        nlWahlartProvincialeStaten = NlWahlart.PROVINCIALE_STATEN1;
      } else if (ElectionSubcategoryType.PS_2.equals(electionSubcategoryType)) {
        nlWahlartProvincialeStaten = NlWahlart.PROVINCIALE_STATEN2;
      } else {
        // fallback ist die Anzahkl der Wahlkreise
        nlWahlartProvincialeStaten = NlWahlart.bestimmeWahlartProvincialeStaten(wahl, bestimmeAnzahlWahlkreise(emlWrapper));
      }

      eml110Mapper.setNlWahlart(wahlart, nlWahlartProvincialeStaten);
      if (NlWahlart.PROVINCIALE_STATEN1.equals(nlWahlartProvincialeStaten)) {
        LOG.info("Provinziale Staaten - nicht mehr als ein Wahlkreis");
      }

      if (NlWahlart.PROVINCIALE_STATEN2.equals(nlWahlartProvincialeStaten)) {
        LOG.info("Provinziale Staaten - mehr als ein Wahlkreis");
      }
    }
  }

  @Nullable
  protected ElectionSubcategoryType versucheSubCategorieZuLesen(EmlWrapper emlWrapper) {
    try {
      var eml110aWrapper = new Eml110aWrapper(emlWrapper.getContent());
      return eml110aWrapper.getElectionIdentifier110a().getElectionSubcategory();
    } catch (Exception e) {
      LOG.warn("Fehler beim Bestimmen der SubCategorie: " + e.getMessage());
    }
    return null;

  }

  private long bestimmeAnzahlWahlkreise(EmlWrapper emlWrapper) {
    return bestimmeAnzahlWahlkreise(emlWrapper.getContent().getElectionEvent().getElection().getElectionTree().getRegion());
  }

  private long bestimmeAnzahlWahlkreise(List<Region> regions) {
    return regions.stream().filter(region -> RegionCategoryType.KIESKRING.equals(region.getRegionCategory())).count();
  }

  protected abstract void postWahlMapping(Wahlobject wahl, EmlWrapper emlWrapper);

  protected List<Gruppe> mappeGruppen(WahlImportContext context, EmlWrapper emlWrapper, Wahlobject wahl, List<WahlParameter> wahlparameter) {
    return eml110Mapper.mappeGruppen(emlWrapper, wahl);
  }

  protected List<Gebiet> mappeGebiete(WahlImportContext context, EmlWrapper emlWrapper, Wahlobject wahl, List<WahlParameter> wahlparameter) {
    return eml110Mapper.mappeGebiete(emlWrapper, wahl, getWurzelgebietAuswahl(context), wahlparameter, context.getMandant());
  }

  protected List<GruppeImGebiet> mappeGruppenImGebiet(List<Gebiet> gebiete, List<Gruppe> gruppen) {
    return new ArrayList<>();
  }

  @Override
  public Violations validateInputFormat(InputStream is, WahlImportContext context, FileKonfiguration fileKonfiguration) {
    var violations = new Violations();

    try {
      var readContent = IOUtils.toByteArray(is);

      // ELECTVAPP-1037
      try {
        EmlValidator.validateEml110a(new ByteArrayInputStream(readContent));
      } catch (Exception e) {
        LOG.warn("EML invalid: " + e.getMessage(), e);
        violations.addWarning("invalid.eml.file");
      }

      var emlWrapper = emlImportMapperHelper.readEmlNoHash(new ByteArrayInputStream(readContent));

      if (!getEmlId().equalsIgnoreCase(emlWrapper.getContent().getId())) {
        violations.addError("eml.110a.id.falsch", emlWrapper.getContent().getId());

        return violations;
      }

      validiereWahlartUndDatum(emlWrapper, violations, context);
      if (violations.containsViolationsWithLevelError()) {
        return violations;
      }

      if (emlWrapper.getContent().getElectionEvent().getElection().getPreferenceThreshold() == null) {
        // ELECTVAPP-968
        violations.addError("eml.110a.preference.theshold.missing", emlWrapper.getContent().getId());
        return violations;
      }

    } catch (IOException e) {
      LOG.warn("Error reading eml: " + e.getMessage(), e);
      violations.addError("exception.unknown.message");

    }

    return violations;
  }

  protected abstract void validiereWahlartUndDatum(EmlWrapper emlWrapper, Violations violations, WahlImportContext context);

  @Override
  public void addProperties(FormBuilder formBuilder, WahlImportContext context, @Nullable Datei datei) {
    if (datei != null) {
      try {
        try (var is = emlImportMapperHelper.getInputStream(datei)) {
          addProperties(formBuilder, context, is);
        }
      } catch (Exception e) {
        LOG.warn("Fehler beim Lesen der Datei: " + datei + ", " + e.getMessage(), e);
        formBuilder.addProperty(new MessageProperty(getMessage("message.unknown.error")));
      }
    }

    if (isAlsWahldefinitionImportierenMoeglich()) {
      formBuilder.addProperty(
          new LambdaBooleanProperty(getMessage("eml110.wahldefinition.importieren"), false, value -> setAlsWahldefinitionImportieren(context, value),
              () -> getAlsWahldefinitionImportieren(context)));
    }
  }

  public void addProperties(FormBuilder formBuilder, WahlImportContext context, InputStream is) {
    var emlWrapper = emlImportMapperHelper.readEmlHash(is,
        emlImportMapperHelper.getHashCodeProperty(formBuilder, context,
            x -> EmlValidation.BESTAETIGEN/* siehe https://confluence.ivu.de/display/VAPP/EML-Import+validation / ELECT-10409 */));

    var wahl = eml110Mapper.mappeWahl(emlWrapper);

    // ELECT-9760 und ELECT-13455 "PS" Super-Duper-Spezial-Wahlartmapping
    adjustPSWahlart(wahl, wahl.getWahlart(), emlWrapper);

    var wahlartAusDatei = wahl.getWahlart();

    var wahlartInDb = nlWahlImportHelper.findeWahlartInDb(wahlartAusDatei, context);

    Map<NlGebietsart, Boolean> ebenenCache = new HashMap<>();

    var gebiete = eml110Mapper.getGebieteZurAuswahl(emlWrapper, nlGebietsart -> isGebietsartZurAuswahl(nlGebietsart, wahlartInDb, ebenenCache));

    var msgKeyWurzelgebiet = EML_110_WURZELGEBIET_LABEL;

    LOG.info("gebiete: " + gebiete);

    if (isPsb(context)) { // ELECTVAPP-1398
      msgKeyWurzelgebiet = "eml110.wurzelgebiet.label.psb";
      gebiete = filterAbGebiete(context, gebiete);
      LOG.info("gebiete gefiltert: " + gebiete);

      if (NlWahlart.EILANDSRAAD.equalsWahlart(wahlartInDb)) {
        msgKeyWurzelgebiet = "eml110.wurzelgebiet.label.psb.eilandsraad";
      }
      if (NlWahlart.EERSTE_KAMER.equalsWahlart(wahlartInDb)) {
        msgKeyWurzelgebiet = EML_110_WURZELGEBIET_LABEL; // hier keine Gemeinde
      }
    }

    if (gebiete.size() == 1) {
      var wurzelgebiet = gebiete.get(0);

      setWurzelgebietAuswahl(context, wurzelgebiet);

      formBuilder.addProperty(new ReadOnlyStringProperty(getMessage(msgKeyWurzelgebiet), getItemLabelRegion(wurzelgebiet)));
    } else {
      var gebieteSortiert = gebiete.stream().sorted(Comparator.comparing(this::getItemLabelRegion)).collect(Collectors.toList());

      formBuilder.addProperty(new LambdaStringSelectOneProperty(getMessage(msgKeyWurzelgebiet), !isAlsWahldefinitionImportierenMoeglich(),
          value -> setWurzelgebietAuswahl(context, value),
          () -> getWurzelgebietAuswahl(context), gebieteSortiert, true) {

        @Override
        public String getItemLabel(String item) {
          return getItemLabelRegion(item);
        }
      });
    }
  }

  private List<String> filterAbGebiete(WahlImportContext context, List<String> gebiete) {

    var gebiet = findeObersterstesGebietABWahl(context);

    if (gebiet == null) {
      return gebiete;
    }

    return gebiete.stream().filter(vorschlag -> vorschlag.startsWith(gebiet.getName() + "|")).findFirst().map(Collections::singletonList).orElse(emptyList());
  }

  @Nullable
  private Gebiet findeObersterstesGebietABWahl(WahlImportContext context) {
    return wahlDAO.findByAll(context.getMandant()).stream().filter(NlWahlart.WATERSCHAP::equalsWahlart)
        .map(wahl -> wahl.getWurzelgebiet(EntityMitWurzelgebietDAO.fromDao(gebietDAO)))
        .filter(Objects::nonNull).findFirst().orElse(null);
  }

  private boolean isPsb(WahlImportContext context) {
    try {
      return nlSystemProperties.getDefaultElectionLevel(context.getMandant()).isPSB();
    } catch (Exception ignored) {
    }

    return false;
  }

  private String getItemLabelRegion(String regionNameType) {
    if (StringUtils.isBlank(regionNameType)) {
      return regionNameType;
    }

    if (regionNameType.indexOf('|') > -1) {
      return regionNameType.substring(0, regionNameType.indexOf('|'));
    }

    return regionNameType;
  }

  private boolean isGebietsartZurAuswahl(NlGebietsart nlGebietsart, Wahlart wahlartInDb, Map<NlGebietsart, Boolean> ebenenCache) {
    if (NlGebietsart.GEMEENTE.equals(nlGebietsart) && NlWahlart.EILANDSRAAD.equalsWahlart(wahlartInDb)) {
      // ELECT-13497
      return isGebietsartZurAuswahlInternal(NlGebietsart.OPENBAAR_LICHAAM, wahlartInDb, ebenenCache);
    }
    return isGebietsartZurAuswahlInternal(nlGebietsart, wahlartInDb, ebenenCache);
  }

  private Boolean isGebietsartZurAuswahlInternal(NlGebietsart nlGebietsart, Wahlart wahlartInDb, Map<NlGebietsart, Boolean> ebenenCache) {
    return ebenenCache.computeIfAbsent(nlGebietsart, x -> {
      var wahlgebiete = wahlartInDb.getGebietsarteigenschaften().stream().filter(Gebietsarteigenschaft::getWahlgebiet)
          .collect(Collectors.toSet());

      if (!wahlgebiete.isEmpty()) {
        return wahlgebiete.stream().map(Gebietsarteigenschaft::getGebietsart).anyMatch(nlGebietsart::equalsGebietsart);
      }
      var listenwahlgebiete = wahlartInDb.getGebietsarteigenschaften().stream().filter(Gebietsarteigenschaft::getListenwahlgebiet)
          .collect(Collectors.toSet());

      if (!listenwahlgebiete.isEmpty()) {
        return listenwahlgebiete.stream().map(Gebietsarteigenschaft::getGebietsart).anyMatch(nlGebietsart::equalsGebietsart);
      }
      return false;

      //      SystemGebietsebene systemGebietsebene = konfigurationLoader
      //          .resolveEnumValue(mandant, MandantKonfigurationWerte.SYSTEM_GEBIETSEBENE, SystemGebietsebene.class);
      //      return gebietsartDAO.findByAll(mandant).stream()
      //          .anyMatch(gebietsart -> gebietsart.getSystemGebietsebene().isWurzelGebietsebene(systemGebietsebene) && nlGebietsart.equalsGebietsart(gebietsart));

    });
  }

  private void setWurzelgebietAuswahl(WahlImportContext context, String value) {
    context.put(CONTEXT_PARAM_WURZELGEBIET, value);
  }

  private String getWurzelgebietAuswahl(WahlImportContext context) {
    return context.get(CONTEXT_PARAM_WURZELGEBIET, String.class);
  }

  private Boolean getAlsWahldefinitionImportieren(WahlImportContext context) {
    return context.get(CONTEXT_PARAM_ALS_WAHLDEFINITION_IMPORTIEREN, Boolean.class);
  }

  private void setAlsWahldefinitionImportieren(WahlImportContext context, Boolean value) {
    context.put(CONTEXT_PARAM_ALS_WAHLDEFINITION_IMPORTIEREN, value);
  }

  protected abstract boolean isAlsWahldefinitionImportierenMoeglich();

  public String getMessage(String msgKey, Object... params) {
    return messages.getMessageForMessageKey(msgKey, params);
  }
}
