package de.ivu.elect.nl.interfaces.gbav.mapper;

import de.ivu.elect.nl.competent.gbav.v1.Categorie;
import de.ivu.elect.nl.competent.gbav.v1.Persoon;
import de.ivu.elect.nl.competent.gbav.v1.Rubriek;
import de.ivu.elect.nl.interfaces.gbav.GbavConstants;
import de.ivu.elect.nl.interfaces.gbav.GbavInterfacePersonenDaten;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.Date;

public class GbavPersoonMapper {

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

  @Inject
  protected transient GbavNationaliteitMapper gbavNationaliteitMapper;

  @Inject
  protected transient GbavAdellijkeTitelMapper gbavAdellijkeTitelMapper;

  @Inject
  protected transient GbavDateMapper gbavDateMapper;


  public GbavInterfacePersonenDaten mapPersoonToGbavPersonenDaten(Persoon persoon, Date electionDate) {
    GbavInterfacePersonenDaten result = new GbavInterfacePersonenDaten();
    //    result.setPersonInfoType(EPersonInfoType.GBAV_PERSON);

    String lastDateOfBreakup = "19000101"; // Jan 1st 1900
    for (Categorie categorie : persoon.getCategorieen().getCategorie()) {
      if (GbavConstants.CATEGORIE_PERSOON.equals(categorie.getNummer())) {
        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_BSN.equals(rubriek.getNummer())) {
            result.setOrdnungsmerkmal(rubriek.getWaarde());
          } else if (GbavConstants.RUBRIEK_GESLACHTSNAAM.equals(rubriek.getNummer())) {
            result.setFamilienname(rubriek.getWaarde());
          } else if (GbavConstants.RUBRIEK_VOORVOEGSEL_GESLACHTSNAAM.equals(rubriek.getNummer())) {
            result.setFamiliennameBestandteil(rubriek.getWaarde());
          } else if (GbavConstants.RUBRIEK_VOORNAMEN.equals(rubriek.getNummer())) {
            String voornamen = rubriek.getWaarde();
            result.setVorname(voornamen);
            result.setInitialen(mapVoornamenToInitials(voornamen));
          } else if (GbavConstants.RUBRIEK_GEBOORTEDATUM.equals(rubriek.getNummer())) {
            String geboortedatum = rubriek.getWaarde();
            if (geboortedatum.endsWith("00")) {
              result.setGeburtsdatumErfasst(gbavDateMapper.mapGbavDateToDateAnnex(geboortedatum));
            } else {
              result.setGeburtsdatum(gbavDateMapper.mapGbavDateToDate(geboortedatum));
            }
          } else if (GbavConstants.RUBRIEK_GESLACHTSAANDUIDING.equals(rubriek.getNummer())) {
            result.setGeschlecht(GbavGenderMapper.mapGeslachtsaanduidingToGender(rubriek.getWaarde()));
          } else if (GbavConstants.RUBRIEK_ADELLIJKE_TITEL_PREDIKAAT.equals(rubriek.getNummer())) {
            result.setTitel(gbavAdellijkeTitelMapper.mapCodeToName(rubriek.getWaarde()));
          }
        }
      } else if (GbavConstants.CATEGORIE_PARTNERSCHAP.equals(categorie.getNummer())) {
        String partnerLastname = null;
        String partnerNamePrefix = null;
        String dateOfBreakup = null;
        String dateOfBreakupGbav = null;
        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_GESLACHTSNAAM.equals(rubriek.getNummer())) {
            partnerLastname = rubriek.getWaarde();
          } else if (GbavConstants.RUBRIEK_VOORVOEGSEL_GESLACHTSNAAM.equals(rubriek.getNummer())) {
            partnerNamePrefix = rubriek.getWaarde();
          } else if (GbavConstants.RUBRIEK_DATUM_ONTBINDING_PARTNERSCHAP.equals(rubriek.getNummer())) {
            dateOfBreakupGbav = rubriek.getWaarde();
            dateOfBreakup = gbavDateMapper.mapGbavDateToDateAnnex(rubriek.getWaarde());
          }
        }
        if (StringUtils.isNotBlank(partnerLastname) || StringUtils.isNotBlank(partnerNamePrefix)) {
          StringBuilder sb = new StringBuilder();
          if (StringUtils.isNotBlank(partnerNamePrefix)) {
            sb.append(partnerNamePrefix);
            sb.append(" ");
          }
          if (StringUtils.isNotBlank(partnerLastname)) {
            sb.append(partnerLastname);
          }
          if (StringUtils.isNotBlank(dateOfBreakup)) {
            sb.append(" (");
            sb.append(dateOfBreakup);
            sb.append(")");
          }
          if (isAfter(dateOfBreakupGbav, lastDateOfBreakup)) {
            lastDateOfBreakup = dateOfBreakupGbav;
            result.setEhepartner(sb.toString());
          }
        }

      } else if (GbavConstants.CATEGORIE_VERBLIJFPLAATS.equals(categorie.getNummer())) {
        String straatnaam = null;
        String huisnummer = null;
        String huisletter = null;
        String huisnummertoevoeging = null;

        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_WOONPLAATSNAAM.equals(rubriek.getNummer())) {
            String woonplaatsnaam = rubriek.getWaarde();
            result.setWohnanschriftOrt(woonplaatsnaam);
            result.setErreichbarkeitsanschriftOrt(woonplaatsnaam);
          } else if (GbavConstants.RUBRIEK_POSTCODE.equals(rubriek.getNummer())) {
            result.setErreichbarkeitsanschriftPlz(GbavPostcodeMapper.mapGbavPostcodeToPostcode(rubriek.getWaarde()));
          } else if (GbavConstants.RUBRIEK_LAND_ADRES_BUITENLAND.equals(rubriek.getNummer())) {
            GbavStaat gbavStaat = GbavStaat.getGbavStaat(rubriek.getWaarde());
            result.setWohnanschriftStaat(gbavStaat);
            // result.setContactCountry(landAdresBuitenland); // contact address abroad not useful
          } else if (GbavConstants.RUBRIEK_REGEL_2_ADRES_BUITENLAND.equals(rubriek.getNummer())) {
            result.setWohnanschriftOrt(rubriek.getWaarde());
          } else if (GbavConstants.RUBRIEK_STRAATNAAM.equals(rubriek.getNummer())) {
            straatnaam = rubriek.getWaarde();
          } else if (GbavConstants.RUBRIEK_HUISNUMMER.equals(rubriek.getNummer())) {
            huisnummer = rubriek.getWaarde();
          } else if (GbavConstants.RUBRIEK_HUISLETTER.equals(rubriek.getNummer())) {
            huisletter = rubriek.getWaarde();
          } else if (GbavConstants.RUBRIEK_HUISNUMMERTOEVOEGING.equals(rubriek.getNummer())) {
            huisnummertoevoeging = rubriek.getWaarde();
          }
        }

        result.setErreichbarkeitsanschriftStrasse(mapToStreet(straatnaam, huisnummer, huisletter, huisnummertoevoeging));
      } else if (GbavConstants.CATEGORIE_NATIONALITEIT.equals(categorie.getNummer())) {
        String nationality = null;
        GbavBesondereNiederlaendischeStaatsbuergerschaft specialNetherlandsNationality = GbavBesondereNiederlaendischeStaatsbuergerschaft.UNBEKANNT;
        boolean nationalityValid = true;

        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_NATIONALITEIT.equals(rubriek.getNummer())) {
            nationality = gbavNationaliteitMapper.mapCodeToName(rubriek.getWaarde());
          } else if (GbavConstants.RUBRIEK_AANDUIDING_BIJZONDER_NEDERLANDERSCHAP.equals(rubriek.getNummer())) {
            String aanduidingBijzonderNederlandshap = rubriek.getWaarde();

            if (GbavConstants.VALUE_AANDUIDING_BIJZONDER_NEDERLANDERSCHAP_BEHANDELD_ALS_NEDERLANDER.equals(aanduidingBijzonderNederlandshap)) {
              specialNetherlandsNationality = GbavBesondereNiederlaendischeStaatsbuergerschaft.FESTGESTELLT_UND_ZU_BEHANDELN_ALS_NIEDERLAENDER;
            } else if (GbavConstants.VALUE_AANDUIDING_BIJZONDER_NEDERLANDERSCHAP_VASTGESTELD_NIET_NEDERLANDER.equals(aanduidingBijzonderNederlandshap)) {
              specialNetherlandsNationality = GbavBesondereNiederlaendischeStaatsbuergerschaft.FESTGESTELLT_UND_NICHT_ZU_BEHANDELN_ALS_NIEDERLAENDER;
            }
          } else if (GbavConstants.RUBRIEK_INGANGSDATUM_GELDIGHEID.equals(rubriek.getNummer())) {
            Date date = gbavDateMapper.mapGbavDateToDate(rubriek.getWaarde());

            if (date != null) {
              nationalityValid = date.before(electionDate);
            }
          }
        }

        if (nationalityValid) {
          result.setStaatsangehoerigkeit(nationality);
          result.setBesondereNiederlaendischeStaatsbuergerschaft(specialNetherlandsNationality);
        }
      } else if (GbavConstants.CATEGORIE_OVERLIJDEN.equals(categorie.getNummer())) {
        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_DATUM_OVERLIJDEN.equals(rubriek.getNummer())) {
            result.setSterbedatum(gbavDateMapper.mapGbavDateToDate(rubriek.getWaarde()));
          }
        }
      } else if (GbavConstants.CATEGORIE_KIESRECHT.equals(categorie.getNummer())) {
        for (Rubriek rubriek : categorie.getRubrieken().getRubriek()) {
          if (GbavConstants.RUBRIEK_AANDUIDING_UITGESLOTEN_KIESRECHT.equals(rubriek.getNummer())) {
            result.setWahlrechtsausschluss(isVonWahlAusgeschlossen(rubriek.getWaarde()));
          } else if (GbavConstants.RUBRIEK_EINDDATUM_UITSLUITING_KIESRECHT.equals(rubriek.getNummer())) {
            // if this date is in past, the person is not excluded from election anymore
            if (electionDate == null) {
              LOG.warn("Das Wahldatum ist null und daher konnte es nicht festgestellt werden, ob der Kandidat von der Wahl ausgeschlossen sei.");

              result.setWahlrechtsausschluss(GbavWahlrechtsausschluss.UNBEKANNT);

              break;
            }

            String einddatumUitsluitingKiesrecht = rubriek.getWaarde();

            if (isDateBeforeElectionDate(einddatumUitsluitingKiesrecht, electionDate)) {
              result.setWahlrechtsausschluss(GbavWahlrechtsausschluss.NICHT_VORHANDEN);

              break;
            }
          }
        }
      }
    }

    return result;
  }

  private GbavWahlrechtsausschluss isVonWahlAusgeschlossen(String rubriekWert) {
    if (StringUtils.isBlank(rubriekWert)) {
      return GbavWahlrechtsausschluss.UNBEKANNT;
    } else if (GbavConstants.VALUE_AANDUIDING_UITGESLOTEN_KIESRECHT_UITGESLOTEN.equals(rubriekWert)) {
      return GbavWahlrechtsausschluss.VORHANDEN;
    }

    return GbavWahlrechtsausschluss.NICHT_VORHANDEN;
  }

  /**
   * <code>null</code> always represents the latest date.
   *
   * @param dateOfBreakupGbav is a breakup date in GBAV format. This has to be compared to the so far latest date of a breakup
   * @param lastDateOfBreakup so far latest date of a breakup
   * @return <code>true</code> if dateOfBreakupGbav is <code>null</code> or a date after lastDateOfBreakup
   */
  private boolean isAfter(String dateOfBreakupGbav, String lastDateOfBreakup) {
    if (StringUtils.isBlank(dateOfBreakupGbav)) {
      return true;
    }
    if (StringUtils.isBlank(lastDateOfBreakup)) {
      return false;
    }
    return dateOfBreakupGbav.compareTo(lastDateOfBreakup) > 0;
  }

  private boolean isDateBeforeElectionDate(String gbavDate, Date electionDate) {
    Date date = gbavDateMapper.mapGbavDateToDate(gbavDate);
    if (date == null || electionDate == null) {
      return false;
    }
    return electionDate.after(date);
  }

  private String mapToStreet(String straatnaam, String huisnummer, String huisletter, String huisnummertoevoeging) {

    StringBuilder sb = new StringBuilder();

    if (StringUtils.isNotBlank(straatnaam)) {
      sb.append(straatnaam);
    }
    if (StringUtils.isNotBlank(huisnummer)) {
      if (sb.length() > 0) {
        sb.append(" ");
      }
      sb.append(huisnummer);
    }
    if (StringUtils.isNotBlank(huisletter)) {
      if (sb.length() > 0) {
        sb.append(" ");
      }
      sb.append(huisletter);
    }
    if (StringUtils.isNotBlank(huisnummertoevoeging)) {
      if (sb.length() > 0) {
        sb.append(" ");
      }
      sb.append(huisnummertoevoeging);
    }

    return sb.toString();
  }


  private String mapVoornamenToInitials(String voornamen) {
    if (StringUtils.isBlank(voornamen)) {
      return null;
    }
    StringBuilder sb = new StringBuilder();
    for (String name : StringUtils.split(voornamen, " ")) {
      if (StringUtils.isNotBlank(name)) {
        sb.append(name, 0, 1);
        sb.append(".");
      }
    }
    return sb.toString();
  }
}
