
package de.ivu.elect.business.kandidatur.control;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;

import java.io.Serializable;
import java.util.Date;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
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.entity.PersonStatus;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.person.entity.PersonenregisterAbgleichStatus;
import de.ivu.elect.business.personenpruefung.boundary.PersonenPruefungStarter;
import de.ivu.elect.business.personenpruefung.control.CorePersonenPruefungVorbereiter;
import de.ivu.elect.business.personenpruefung.control.KandidaturPruefungAttributeChangedChecker;

/**
 * ELECT-10305
 */
public class NlListenkandidaturSynchronisieren implements Serializable {

  private static final long serialVersionUID = 1L;

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

  @Inject
  private transient KandidaturPruefungAttributeChangedChecker kandidaturPruefungAttributeChangedChecker;

  @Inject
  private transient CorePersonenPruefungVorbereiter personenPruefungVorbereiter;

  @Inject
  private transient PersonenPruefungStarter personenPruefungStarter;

  public NlListenkandidaturSynchronisieren() {
    super();
  }

  public Result synchronisiereVerknuepftePersonen(Personendaten von, boolean kandidaturStatusSynchronisieren, boolean positionenSynchronisieren, boolean startePersonenregisterAbgleich) {
    info("Synchronisiere [kandidaturStatusSynchronisieren = {}, positionenSynchronisieren = {}, startePersonenregisterAbgleich = {}]", kandidaturStatusSynchronisieren, positionenSynchronisieren,
        startePersonenregisterAbgleich);

    Result result = new Result(startePersonenregisterAbgleich);
    result = result.merge(synchronisiereVerknuepftePersonen(von, kandidaturStatusSynchronisieren, positionenSynchronisieren));

    if (von.isInstanceOf(Listenkandidatur.class)) {
      Listenkandidatur vonLk = castTo(von, Listenkandidatur.class);

      if (startePersonenregisterAbgleich) {
        prepareListenkandidaturPersonenregisterAbgleich(vonLk);
      }

      if (vonLk.hasZugeordneteErsatzkandidatur()) { // Ersatzkandidatur (== "Kandidatenbevollmächtigter") wird auf einer Seite zusammen mit der Listenkandidatur bearbeitet!
        result = result.merge(synchronisiereVerknuepftePersonen(vonLk.getZugeordneteErsatzkandidatur(), kandidaturStatusSynchronisieren, positionenSynchronisieren));
      }
    }

    if (result.isPersonenregisterAbgleichNotwendig()) {
      info("Starte Personenregisterabgleich");

      starteListenkandidaturPersonenregisterAbgleich();
    }

    return result;
  }

  protected Result synchronisiereVerknuepftePersonen(Personendaten von, boolean kandidaturStatusSynchronisieren, boolean positionenSynchronisieren) {
    info("\t Synchronisiere {}", von);

    Listenkandidatur vonVerknuepfteListenkandidaturPersonenregisterAbzugleichen = null; // ELECT-11635

    for (Personendaten nach : von.getIdentischVerknuepftePersonen()) {
      if (!von.equals(nach)) {
        synchronisierePerson(von, nach, kandidaturStatusSynchronisieren, positionenSynchronisieren);

        if (nach.isInstanceOf(Listenkandidatur.class)) {
          Listenkandidatur nachLk = castTo(nach, Listenkandidatur.class);

          if (isVerknuepfteListenkandidaturPersonenregisterAbzugleichen(von, nachLk)) {
            vonVerknuepfteListenkandidaturPersonenregisterAbzugleichen = nachLk;
          }
        }
      }
    }

    if (vonVerknuepfteListenkandidaturPersonenregisterAbzugleichen != null) {
      info("\t Markiere verknüpfte Listenkandidatur zum erneuten Personenregisterabgleich {}", vonVerknuepfteListenkandidaturPersonenregisterAbzugleichen);

      prepareListenkandidaturPersonenregisterAbgleich(vonVerknuepfteListenkandidaturPersonenregisterAbzugleichen);

      return new Result(true);
    }

    return new Result(false);
  }

  protected void synchronisierePerson(Personendaten von, Personendaten nach, boolean kandidaturStatusSynchronisieren, boolean positionenSynchronisieren) {
    info("\t\t {}", nach);

    PersonStatus personStatus = nach.getPersonStatus();
    PersonenregisterAbgleichStatus personenregisterAbgleichStatus = nach.getPersonenregisterAbgleichStatus();
    Date personenregisterAbgleichZeitpunkt = nach.getPersonenregisterAbgleichZeitpunkt();

    KandidaturStatus kandidaturStatus = null;
    String kandidaturStatusBemerkung = nach.getBemerkung();

    Long vorgegebenePosition = null;
    Long eingereichtePosition = null;
    Long relativePosition = null;

    if (nach.isInstanceOf(Kandidatur.class)) {
      Kandidatur nachK = castTo(nach, Kandidatur.class);

      kandidaturStatus = nachK.getKandidaturStatus();

      if (nachK.isInstanceOf(Listenkandidatur.class)) {
        Listenkandidatur nachLk = castTo(nachK, Listenkandidatur.class);

        vorgegebenePosition = nachLk.getVorgegebenePosition();
        eingereichtePosition = nachLk.getEingereichtePosition();
        relativePosition = nachLk.getRelativePosition();
      }
    }

    von.copyEntityAttributesUsingSetter(nach);
    von.getAnschrift().copyEntityAttributesUsingSetter(nach.getAnschrift());
    von.getErreichbarkeitsanschrift().copyEntityAttributesUsingSetter(nach.getErreichbarkeitsanschrift());

    nach.setPersonStatus(personStatus);
    nach.setPersonenregisterAbgleichStatus(personenregisterAbgleichStatus);
    nach.setPersonenregisterAbgleichZeitpunkt(personenregisterAbgleichZeitpunkt);

    // ELECT-11275, KandidaturStatus und "Bemerkung zur Nichtzulassung" nicht automatisch synchronisieren...
    nach.setBemerkung(kandidaturStatusBemerkung);

    if (nach.isInstanceOf(Kandidatur.class)) {
      Kandidatur nachK = castTo(nach, Kandidatur.class);

      nachK.setKandidaturStatus(kandidaturStatus);

      if (nachK.isInstanceOf(Listenkandidatur.class)) {
        Listenkandidatur nachLk = castTo(nachK, Listenkandidatur.class);

        nachLk.setVorgegebenePosition(vorgegebenePosition);
        nachLk.setEingereichtePosition(eingereichtePosition);
        nachLk.setRelativePosition(relativePosition);
      }
    }

    if (von.isInstanceOf(Listenkandidatur.class)) {
      Listenkandidatur vonLk = castTo(von, Listenkandidatur.class);

      if (nach.isInstanceOf(Listenkandidatur.class)) {
        Listenkandidatur nachLk = castTo(nach, Listenkandidatur.class);

        info("\t\t\t Synchronisiere PersonStatus inklusive PersonenregisterAbgleichStatus [personStatus = {}, personenregisterAbgleichStatus = {}, personenregisterAbgleichZeitpunkt = {}]",
            vonLk.getPersonStatus(), vonLk.getPersonenregisterAbgleichStatus(), vonLk.getPersonenregisterAbgleichZeitpunkt());

        nachLk.setPersonStatus(vonLk.getPersonStatus());
        nachLk.setPersonenregisterAbgleichStatus(vonLk.getPersonenregisterAbgleichStatus());
        nachLk.setPersonenregisterAbgleichZeitpunkt(vonLk.getPersonenregisterAbgleichZeitpunkt());

        if (kandidaturStatusSynchronisieren) {
          info("\t\t\t Synchronisiere KandidaturStatus inklusive Bemerkung [kandidaturStatus = {}, bemerkung = {}]", vonLk.getKandidaturStatus(), vonLk.getBemerkung());

          nachLk.setKandidaturStatus(vonLk.getKandidaturStatus());
          nachLk.setBemerkung(vonLk.getBemerkung());
        }

        if (positionenSynchronisieren) {
          info("\t\t\t Synchronisiere Positionen [vorgegebenePosition ={}, eingereichtePosition = {}, relativePosition = {}]", vonLk.getVorgegebenePosition(), vonLk.getEingereichtePosition(),
              vonLk.getRelativePosition());

          nachLk.setVorgegebenePosition(vonLk.getVorgegebenePosition());
          nachLk.setEingereichtePosition(vonLk.getEingereichtePosition());
          nachLk.setRelativePosition(vonLk.getRelativePosition());
        }
      }
    }
  }

  private boolean isVerknuepfteListenkandidaturPersonenregisterAbzugleichen(Personendaten von, Listenkandidatur nach) {
    if (!von.isInstanceOf(Listenkandidatur.class)) { // Relevante Änderungen an einer Listenkandidatur führen bereits automatisch zu einem neuen Abgleich aller identisch verknüpften Listenkandidaturen...
      kandidaturPruefungAttributeChangedChecker.bestimmeAttributeChanged(nach);

      return nach.isPersonStatusGeeignet() && kandidaturPruefungAttributeChangedChecker.isPersonenregisterAbgleichAttributeChanged();
    }

    return false;
  }

  private void prepareListenkandidaturPersonenregisterAbgleich(Listenkandidatur listenkandidatur) {
    personenPruefungVorbereiter.prepareKandidaturPruefung(listenkandidatur, PREPARE_KANDIDATUR_PERSONENREGISTER_ABGLEICH);
  }

  private void starteListenkandidaturPersonenregisterAbgleich() {
    personenPruefungStarter.startePersonenPruefung();
  }

  private void info(String message, Personendaten personendaten) {
    Gebiet gebiet = personendaten.getGebiet();

    info(message,
        personendaten.getClass().getSimpleName() + " [id = " + personendaten.getId() + ", familienname = " + personendaten.getFamilienname() + ", vornamen = " + personendaten.getVornamen()
            + ", gebiet = " + (gebiet == null ? null : gebiet.getNameMitNummer()) + ", personStatus = " + personendaten.getPersonStatus() + ", personenregisterAbgleichStatus = "
            + personendaten.getPersonenregisterAbgleichStatus() + ", raw = " + personendaten + "]");
  }

  private void info(String message, Object... params) {
    // ELECT-11624 LOG.info(message, params);
  }

  public static final KandidaturPruefungAttributeChangedChecker PREPARE_KANDIDATUR_PERSONENREGISTER_ABGLEICH = new KandidaturPruefungAttributeChangedChecker() {

    @Override
    public boolean isPersonenregisterAbgleichAttributeChanged() {
      return true;
    }

    @Override
    public boolean isDublettenAttributeChanged() {
      return false;
    }

    @Override
    public boolean isMehrstaatigkeitAttributeChanged() {
      return false;
    }

    @Override
    public boolean isWahlrechtAttributeChanged() {
      return false;
    }

    @Override
    public boolean isKontodatenaenderungBearbeiterBestaetigungErforderlichAttributeChanged() {
      return false;
    }

    @Override
    public boolean isKontoverbindungMehrfachAngegebenAttributeChanged() {
      return false;
    }

    @Override
    public boolean isWertelistenAbgleichAttributeChanged() {
      return false;
    }
  };

  public static final class Result {

    private final boolean personenregisterAbgleichNotwendig;

    public Result(boolean personenregisterAbgleichNotwendig) {
      super();

      this.personenregisterAbgleichNotwendig = personenregisterAbgleichNotwendig;
    }

    public boolean isPersonenregisterAbgleichNotwendig() {
      return personenregisterAbgleichNotwendig;
    }

    public Result merge(Result other) {
      return new Result(isPersonenregisterAbgleichNotwendig() || other.isPersonenregisterAbgleichNotwendig());
    }
  }
}
