
package de.ivu.elect.wus.exchange.kandidatur.landtagswahl;

import static de.ivu.elect.wus.exchange.wahlvorschlag.WahlvorschlagMappers.mapToWahlvorschlaege;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

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

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.anschrift.entity.Anschrift;
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.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.GruppeArt;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.kandidatur.control.WahlvorschlagImportContext;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.KandidaturStatus;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.person.boundary.PersonendatenVerknuepfer;
import de.ivu.elect.business.person.entity.VerknuepfungsArt;
import de.ivu.elect.business.person.entity.VerknuepfungsStatus;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagStatus;
import de.ivu.elect.exchange.CsvImportMapper;
import de.ivu.elect.exchange.csv.CsvMarshaller;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.wus.exchange.kandidatur.AbstractKandidaturImportMapper;
import de.ivu.elect.wus.exchange.wahlvorschlag.WahlvorschlagImportMapper;

public abstract class AbstractBayernLandtagswahlWahlvorschlagImportMapper extends AbstractKandidaturImportMapper
    implements
      WahlvorschlagImportMapper,
      CsvImportMapper<List<Wahlvorschlag>, WahlvorschlagImportContext> {

  public static final String FILE_FORMAT_NAME = "file.format.kandidatur.wus.landtagswahl.bayern";

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

  @Inject
  protected transient CsvMarshaller csvMarshaller;

  @Inject
  protected transient PersonendatenVerknuepfer personendatenVerknuepfer;

  public AbstractBayernLandtagswahlWahlvorschlagImportMapper() {
    super();
  }

  @Override
  public List<Wahlvorschlag> deserialize(InputStream is, WahlvorschlagImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    List<Kandidatur> result = new ArrayList<>();

    for (BayernLandtagswahlStimmkreisStimmzettelCsvDTO from : deserialize(is, fileKonfiguration)) {

      Kandidatur listenkandidatur = mapListenkandidatur(from, context);
      result.add(listenkandidatur);

      if (hasDirektkandidat(from)) {
        Kandidatur direktkandidaturkandidatur = mapDirektkandidatur(from, context);
        result.add(direktkandidaturkandidatur);

        erstelleVerknuepfung(direktkandidaturkandidatur, listenkandidatur);
      }
    }

    vereinheitlicheListen(result);

    vereinheitlicheGruppeImGebiet(result);

    return mapToWahlvorschlaege(result);
  }

  private void erstelleVerknuepfung(Kandidatur direktkandidatur, Kandidatur listenkandidatur) {
    if (direktkandidatur == null) {
      throw new IllegalArgumentException("Direktkandidatur darf nicht null sein!");
    }

    if (listenkandidatur == null) {
      throw new IllegalArgumentException("Listenkandidatur darf nicht null sein!");
    }

    personendatenVerknuepfer.verknuepfeMaschinell(VerknuepfungsArt.WAHLVORSCHLAG_DUBLETTE, direktkandidatur, listenkandidatur, VerknuepfungsStatus.IDENTISCH);
  }

  private List<BayernLandtagswahlStimmkreisStimmzettelCsvDTO> deserialize(InputStream is, FileKonfiguration fileKonfiguration) {
    return csvMarshaller.deserialize(getCsvEntryClass(), is, fileKonfiguration);
  }

  private Kandidatur mapDirektkandidatur(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, WahlvorschlagImportContext mucContext) {
    Direktkandidat direktkandidat = new Direktkandidat();
    // Direktkandidaten werden bei LTW nicht durch München geprüft (obwohl sie auf Stimmkreisebene antreten!), daher zulassen
    direktkandidat.setWahlvorschlagStatus(WahlvorschlagStatus.ZUGELASSEN);
    direktkandidat.setGruppeImGebiet(getGruppeImGebietDirektkandidat(from));

    Direktkandidatur kandidatur = direktkandidaturFactory.create();
    kandidatur.setDirektkandidat(direktkandidat);
    kandidatur.setKandidaturStatus(KandidaturStatus.ZUGELASSEN);

    mapAllgemeineKandidaturDaten(from, kandidatur);

    return kandidatur;
  }

  private boolean hasDirektkandidat(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from) {
    return StringUtils.isNotBlank(from.getStimmkreisNummerDirektkandidat());
  }

  protected Kandidatur mapListenkandidatur(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, WahlvorschlagImportContext mucContext) {
    Listenkandidatur kandidatur = listenkandidaturFactory.create();

    kandidatur.setListe(getListe(from, getGruppeImGebietListenkandidat(from, mucContext)));
    kandidatur.setRelativePosition(from.getListenkandidaturPosition());
    // Listen werden bei LTW nicht durch München geprüft, daher zulassen
    kandidatur.setKandidaturStatus(KandidaturStatus.ZUGELASSEN);

    mapAllgemeineKandidaturDaten(from, kandidatur);

    return kandidatur;
  }

  private void mapAllgemeineKandidaturDaten(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, Kandidatur kandidatur) {
    kandidatur.setBerufUnformatiert(from.getPersonBeruf());
    kandidatur.setGeburtsdatum(createDatumFromJahr(from.getPersonGeburtsjahr()));
    kandidatur.setTitel(from.getPersonTitel());
    kandidatur.setFamilienname(from.getPersonFamilienname());
    kandidatur.setRufname(from.getPersonVorname());
    kandidatur.setAnschrift(createAnschrift(from));
    kandidatur.setErreichbarkeitsanschrift(anschriftFactory.create());
  }

  private Anschrift createAnschrift(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from) {
    Anschrift anschrift = anschriftFactory.create();
    anschrift.setOrt(from.getAnschriftOrt());
    anschrift.setPlz(from.getAnschriftPlz());
    anschrift.setStrasse(from.getAnschriftStrasseHausnummer());
    return anschrift;
  }

  private Date createDatumFromJahr(String personGeburtsjahr) {
    if (StringUtils.isNotBlank(personGeburtsjahr) && StringUtils.isNumeric(personGeburtsjahr)) {
      Calendar instance = GregorianCalendar.getInstance();
      instance.set(Integer.valueOf(personGeburtsjahr), GregorianCalendar.JANUARY, 1);
      return instance.getTime();
    }
    return null;
  }

  private Liste getListe(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, GruppeImGebiet gruppeImGebiet) {
    Liste liste = new Liste();
    liste.setGruppeImGebiet(gruppeImGebiet);
    // Listen werden bei LTW nicht durch München geprüft, daher zulassen
    liste.setWahlvorschlagStatus(WahlvorschlagStatus.ZUGELASSEN);
    return liste;
  }

  private GruppeImGebiet getGruppeImGebietListenkandidat(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, WahlvorschlagImportContext mucContext) {
    GruppeImGebiet result = gruppeImGebietFactory.create();

    result.setGebiet(createGebietWurzelgebiet(from, mucContext));
    result.setGruppe(createGruppe(from));
    result.setRelativePosition(from.getGruppeImGebietPosition());

    return result;
  }

  private Gebiet createGebietWurzelgebiet(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from, WahlvorschlagImportContext mucContext) {
    Gebiet gebiet = gebietFactory.create();
    Gebiet obergebiet = mucContext.getWurzelgebiet();

    gebiet.setNummer(obergebiet.getNummer());
    gebiet.setGebietsart(copyGebietsart(obergebiet.getGebietsart()));

    return gebiet;
  }

  /**
   * Die Gebietsart am Context stammt aus irgend einem DAO; die Mapper hier sollen jedoch Entitäten anlegen, die erstmal nichts mit der Datenbank zu tun haben...
   */
  protected Gebietsart copyGebietsart(Gebietsart gebietsart) {
    Gebietsart result = new Gebietsart();
    result.setName(gebietsart.getName());
    return result;
  }

  private GruppeImGebiet getGruppeImGebietDirektkandidat(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from) {
    GruppeImGebiet result = gruppeImGebietFactory.create();

    result.setGebiet(createGebietStimmkreisDirektkandidat(from));
    result.setGruppe(createGruppe(from));

    return result;
  }

  private Gebiet createGebietStimmkreisDirektkandidat(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from) {
    Gebiet gebiet = gebietFactory.create();
    gebiet.setNummer(from.getStimmkreisNummerDirektkandidat());
    gebiet.setGebietsart(createStimmkreisGebietsart());
    return gebiet;
  }

  protected abstract Gebietsart createStimmkreisGebietsart();

  private Gruppe createGruppe(BayernLandtagswahlStimmkreisStimmzettelCsvDTO from) {
    Gruppe to = politischeGruppeFactory.create();
    to.setGruppeArt(GruppeArt.PARTEI);
    to.setName(from.getGruppeName());
    to.setKurzname(normalizeKurzname(from.getGruppeKurzname()));
    to.setSchluessel(to.getKurzname());
    return to;
  }

  @Nullable
  private String normalizeKurzname(String gruppeKurzname) {
    if (gruppeKurzname == null) {
      return null;
    }
    return gruppeKurzname.replaceAll("^\\(", "").replaceAll("\\)$", "");
  }

  @Override
  public Violations validateInputFormat(InputStream is, WahlvorschlagImportContext context, FileKonfiguration fileKonfiguration) {
    return new Violations();
  }

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

  @Override
  public List<String> getCsvColumnHeaders(WahlvorschlagImportContext context) {
    return csvMarshaller.extractHeader(getCsvEntryClass());
  }

  private Class<BayernLandtagswahlStimmkreisStimmzettelCsvDTO> getCsvEntryClass() {
    return BayernLandtagswahlStimmkreisStimmzettelCsvDTO.class;
  }
}
