
package de.ivu.elect.wus.exchange.wahlvorschlag.csv;

import static java.util.stream.Collectors.toList;

import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.anschrift.entity.Anschrift;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietDescriptor;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.control.WahlvorschlagExportContext;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.person.entity.PersonenVerknuepfung;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;

public class WahlvorschlagCsvInternExternMapper extends AbstractWahlvorschlagCsvInternExternMapper<DefaultWahlvorschlagCsvDTO> {

  public WahlvorschlagCsvInternExternMapper() {
    super();
  }

  @Override
  protected DefaultWahlvorschlagCsvDTO createEntityDTO() {
    return new DefaultWahlvorschlagCsvDTO();
  }

  @Override
  protected void fillGruppe(DefaultWahlvorschlagCsvDTO dto, PolitischeGruppe gruppe, WahlvorschlagExportContext context) {
    super.fillGruppe(dto, gruppe, context);
    // ELECT-3396
    //
    // Der Gruppenschlüssel wird für Einzelbewerber ebenfalls exportiert, da
    // die Gruppe über den Gruppenexport mitpersistiert wird.
    //
    dto.setGruppeSchluessel(gruppe.getSchluessel());
  }

  @Override
  protected void fillGebiet(DefaultWahlvorschlagCsvDTO dto, WahlverwaltungGebietDescriptor gebietDescriptor, WahlvorschlagExportContext context) {
    super.fillGebiet(dto, gebietDescriptor, context);
    dto.setGebietsart(gebietDescriptor.getGebiet().getGebietsart().getName());
  }

  @Override
  protected void fillWahlvorschlag(DefaultWahlvorschlagCsvDTO result, Wahlvorschlag wahlvorschlag, EntityAufStimmzettelDaten entityAufStimmzettelDaten, WahlvorschlagExportContext context) {
    super.fillWahlvorschlag(result, wahlvorschlag, entityAufStimmzettelDaten, context);

    // ELECT-6957 alle Daten sichern
    GruppeImGebiet gruppeImGebiet = wahlvorschlag.getGruppeImGebiet();

    result.setWahlvorschlagPositionRelativ(gruppeImGebiet.getRelativePosition());
    result.setWahlvorschlagPositionVorgegeben(gruppeImGebiet.getVorgegebenePosition());
    result.setWahlvorschlagPositionEingereicht(gruppeImGebiet.getEingereichtePosition());
  }

  @Override
  protected void fillKandidatur(DefaultWahlvorschlagCsvDTO dto, Kandidatur kandidatur, EntityAufStimmzettelDaten entityAufStimmzettelDaten, WahlvorschlagExportContext context) {
    super.fillKandidatur(dto, kandidatur, entityAufStimmzettelDaten, context);

    dto.setKandidaturAkademischerGrad(kandidatur.getAkademischerGradUnformatiert());
    dto.setKandidaturGeschlecht(kandidatur.getGeschlecht());

    dto.setKandidaturEhrenamt(kandidatur.getEhrenamtUnformatiert());
    dto.setKandidaturKuenstlername(kandidatur.getKuenstlername());
    dto.setKandidaturOrdensname(kandidatur.getOrdensname());

    dto.setKandidaturBerufsschluessel(kandidatur.getBerufsschluessel());

    dto.setKandidaturStaatsangehoerigkeiten(kandidatur.getStaatsangehoerigkeitenSorted().stream().map(this::mapToStaatsangehoerigkeit).collect(toList()));

    fillAnschrift(dto, kandidatur.getAnschrift(), context);
    fillErreichbarkeitsanschrift(dto, kandidatur.getErreichbarkeitsanschrift(), context);

    fillDubletten(dto, kandidatur, entityAufStimmzettelDaten, context);

    fillGewaehltVorperiode(dto, kandidatur);
  }

  private void fillGewaehltVorperiode(DefaultWahlvorschlagCsvDTO dto, Kandidatur kandidatur) {
    if (kandidatur.hasGewaehltVorperiode()) {
      dto.setGewaehltVorperiode(kandidatur.getGewaehltVorperiode().getGewaehltStatus().name());
    }
  }

  @Override
  protected void fillGeburtsdaten(DefaultWahlvorschlagCsvDTO dto, Kandidatur kandidatur, WahlvorschlagExportContext context) {
    // ELECT-347 und ELECT-2128
    if (context.isGeburtsdatumNurJahr() || context.isFuerVeroeffentlichung()) {
      dto.setKandidaturGeburtsjahr(kandidatur.getGeburtsdatum(), context.getLocale());
    } else {
      super.fillGeburtsdaten(dto, kandidatur, context);
    }

    if (!context.isFuerVeroeffentlichung()) {
      dto.setKandidaturGeburtsort(kandidatur.getGeburtsort()); // ELECT-12274
    }
  }

  private void fillAnschrift(DefaultWahlvorschlagCsvDTO dto, Anschrift anschrift, WahlvorschlagExportContext context) {
    // ELECT-2128
    if (!context.isFuerVeroeffentlichung()) {
      dto.setKandidaturAnschriftStrasse(anschrift.getStrasse());
      dto.setKandidaturAnschriftHausnummer(anschrift.getHausnummer());
      dto.setKandidaturAnschriftHausnummerzusatz(anschrift.getHausnummerzusatz());
      dto.setKandidaturAnschriftWohnungsnummer(anschrift.getWohnungsnummer());
      dto.setKandidaturAnschriftZustellanweisung(anschrift.getZustellanweisung());

      boolean isEmpty = anschrift.getAnschriftTK().getAllEntityFieldValues().stream().allMatch(e -> e.getValue() == null);

      if (!isEmpty) {
        dto.setKandidaturAnschriftArt(anschrift.getAnschriftArt().name());
      }

      dto.setKandidaturAnschriftPLZ(anschrift.getPlz());
    }

    dto.setKandidaturAnschriftOrt(anschrift.getOrt());
    dto.setKandidaturAnschriftOrtsteil(anschrift.getOrtsteil());
  }

  private void fillErreichbarkeitsanschrift(DefaultWahlvorschlagCsvDTO dto, Anschrift anschrift, WahlvorschlagExportContext context) {
    // ELECT-2128
    if (!context.isFuerVeroeffentlichung()) {
      dto.setKandidaturErreichbarkeitsanschriftStrasse(anschrift.getStrasse());
      dto.setKandidaturErreichbarkeitsanschriftHausnummer(anschrift.getHausnummer());
      dto.setKandidaturErreichbarkeitsanschriftHausnummerzusatz(anschrift.getHausnummerzusatz());
      dto.setKandidaturErreichbarkeitsanschriftWohnungsnummer(anschrift.getWohnungsnummer());
      dto.setKandidaturErreichbarkeitsanschriftZustellanweisung(anschrift.getZustellanweisung());

      dto.setKandidaturErreichbarkeitsanschriftPLZ(anschrift.getPlz());
    }

    dto.setKandidaturErreichbarkeitsanschriftOrt(anschrift.getOrt());
    dto.setKandidaturErreichbarkeitsanschriftOrtsteil(anschrift.getOrtsteil());
  }

  private void fillDubletten(DefaultWahlvorschlagCsvDTO dto, Kandidatur kandidatur, EntityAufStimmzettelDaten entityAufStimmzettelDaten, WahlvorschlagExportContext context) {
    // NOTE, dass das CSV-Format nur den Export /einer/ Dublette zulässt
    Kandidatur verknuepfteKandidatur = getKandidaturDublette(kandidatur, entityAufStimmzettelDaten, context);

    if (verknuepfteKandidatur != null) {
      dto.setKandidaturDubletteGebietsart(verknuepfteKandidatur.getGebiet().getGebietsart().getName());
      dto.setKandidaturDubletteGebietsnummer(verknuepfteKandidatur.getGebiet().getNummer());

      if (!StringUtils.equals(kandidatur.getGruppe().getSchluessel(), verknuepfteKandidatur.getGruppe().getSchluessel())) {
        dto.setKandidaturDubletteGruppeSchluessel(verknuepfteKandidatur.getGruppe().getSchluessel());
      }

      if (verknuepfteKandidatur.isListenkandidatur()) {
        Listenkandidatur verknuepfteListenkandidatur = verknuepfteKandidatur.castTo(Listenkandidatur.class).getEntity();
        dto.setKandidaturDubletteListenposition(bestimmeListenkandidaturPosition(verknuepfteListenkandidatur, entityAufStimmzettelDaten, context));
      }
    }
  }

  @Nullable
  private Kandidatur getKandidaturDublette(Kandidatur kandidatur, EntityAufStimmzettelDaten entityAufStimmzettelDaten, WahlvorschlagExportContext context) {
    for (PersonenVerknuepfung verknuepfung : kandidatur.getPersonenVerknuepfungen()) {

      Personendaten verknuepftePerson = verknuepfung.getOther(kandidatur);
      if (verknuepfung.isIdentisch() && verknuepftePerson.isInstanceOf(Kandidatur.class)) {
        Kandidatur verknuepfteKandidatur = verknuepftePerson.castTo(Kandidatur.class).getEntity();

        if (isKandidaturReferenzierbar(verknuepfteKandidatur, entityAufStimmzettelDaten, context)) {
          return verknuepfteKandidatur;
        }
      }

    }

    return null;
  }

  private boolean isKandidaturReferenzierbar(Kandidatur kandidatur, EntityAufStimmzettelDaten entityAufStimmzettelDaten, WahlvorschlagExportContext context) {
    if (kandidatur.isListenkandidatur()) {
      Listenkandidatur listenkandidatur = kandidatur.castTo(Listenkandidatur.class).getEntity();

      // HINT: Listenkandidaturen können nur über ihre Position vernünftig referenziert werden.
      return bestimmeListenkandidaturPosition(listenkandidatur, entityAufStimmzettelDaten, context) != null;
    }

    return true;
  }
}
