
package de.ivu.elect.presentation.personenregister;

import static java.util.Arrays.asList;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;

import de.ivu.elect.business.anschrift.entity.AnschriftArt;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.person.entity.Personendaten;
import de.ivu.elect.business.personenregister.boundary.AbstractPersonenregisterSearchParameters;
import de.ivu.elect.business.personenregister.boundary.Personenregister;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterByOrdnungsmerkmalSearchParameters;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterByPersonendatenSearchParameters;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterPersonendatenConversationSelect;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterSearchResult;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterSwitch;
import de.ivu.elect.business.personenregister.entity.PersonenregisterAnschrift;
import de.ivu.elect.business.personenregister.entity.PersonenregisterMeta;
import de.ivu.elect.business.personenregister.entity.PersonenregisterPersonendaten;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.wahl.entity.Wahltermin;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.personenregister.table.PersonenregisterPersonendatenSearchResultEntryColumnBuilder;

@OnlineHilfeId(value = "198", mehrfachVergebenOk = true)
public abstract class AbstractPersonenregisterPersonendatenSelectionView<E extends Personendaten>
    extends
      AbstractEntityMitPersonendatenSelectionView<E, PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten>, PersonenregisterPersonendatenSearchResultEntry> {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient PersonenregisterMeta personenregisterMeta;

  @Inject
  private transient PersonenregisterSwitch personenregisterSwitch;

  @Inject
  private transient PersonenregisterPersonendatenConversationSelect personenregisterPersonendatenSelect;

  @Inject
  private transient PersonenregisterByPersonendatenSearchParameters personenregisterByPersonendatenSearchParameters;

  @Inject
  private transient PersonenregisterByOrdnungsmerkmalSearchParameters personenregisterByOrdnungsmerkmalSearchParameters;

  @Inject
  private transient PersonenregisterPersonendatenSearchResultEntryColumnBuilder searchResultEntryColumnBuilder;

  @Inject
  private transient ElectCommonPrincipal principal;

  private Boolean personenregisterErreichbar;

  public AbstractPersonenregisterPersonendatenSelectionView() {
    super();
  }

  protected abstract Wahltermin getWahltermin();

  @Override
  protected abstract AbstractPersonenregisterPersonendatenSelectionViewCreateViewHelper<E> getCreateViewHelper();

  private PersonenregisterPersonendatenConversationSelect getOnSelectionViewTemplateSelect() {
    return personenregisterPersonendatenSelect;
  }

  @Override
  protected void resetOnSelectionViewTemplateSelect() {
    personenregisterPersonendatenSelect.clear();
  }

  @Override
  protected boolean isPersonenregisterVorhanden() {
    return personenregisterSwitch.isPersonenregisterAvailable(getMandant());
  }

  @Override
  protected boolean isPersonenregisterErreichbar() {
    if (personenregisterErreichbar == null) {
      personenregisterErreichbar = getPersonenregister().isErreichbar(getMandant());
    }

    return personenregisterErreichbar;
  }

  private void resetPersonenregisterErreichbar() {
    personenregisterErreichbar = null;
  }

  private Personenregister< ? > getPersonenregister() {
    return personenregisterSwitch.getPersonenregister(getMandant());
  }

  @Override
  public boolean isSearchByPersonendatenSupported() {
    return getPersonenregister().isSearchByPersonendatenManuellSupported(getMandant());
  }

  @Override
  protected PersonenregisterByPersonendatenSearchParameters getSearchByPersonendatenSearchParameters() {
    return personenregisterByPersonendatenSearchParameters;
  }

  @Override
  protected void initDefaultSearchByPersonendatenSearchParameters() {
    initDefaultPersonenregisterSearchParameters(getSearchByPersonendatenSearchParameters());
  }

  @Override
  public boolean isSearchByOrdnungsmerkmalSupported() {
    return getPersonenregister().isSearchByOrdnungsmerkmalManuellSupported(getMandant());
  }

  @Override
  protected PersonenregisterByOrdnungsmerkmalSearchParameters getSearchByOrdnungsmerkmalSearchParameters() {
    return personenregisterByOrdnungsmerkmalSearchParameters;
  }

  @Override
  protected void initDefaultSearchByOrdnungsmerkmalSearchParameters() {
    initDefaultPersonenregisterSearchParameters(getSearchByOrdnungsmerkmalSearchParameters());
  }

  private void initDefaultPersonenregisterSearchParameters(AbstractPersonenregisterSearchParameters personenregisterSearchParameters) {
    personenregisterSearchParameters.setWahltermin(getWahltermin());
    personenregisterSearchParameters.setEinwohnerstatus(PersonenregisterByPersonendatenSearchParameters.DEFAULT_PERSONENREGISTER_EINWOHNERSTATUS);
  }

  @Override
  public PersonenregisterPersonendatenSearchResultEntryColumnBuilder getSearchResultEntryColumnBuilder() {
    return searchResultEntryColumnBuilder;
  }

  @Override
  public boolean isBtnTestErreichbarkeitRendered() {
    return isPersonenregisterVorhanden() && !isPersonenregisterErreichbar();
  }

  @Override
  public boolean isBtnTestErreichbarkeitDisabled() {
    return isPersonenregisterErreichbar();
  }

  @Override
  @Redirect
  public String testErreichbarkeit() {
    resetPersonenregisterErreichbar();

    return super.testErreichbarkeit();
  }

  @Override
  public String getBtnSearchLabel() {
    return getMessage("entity.mit.personendaten.selection.action.search", personenregisterMeta.getKurznameEinzahlMessage());
  }

  @Override
  protected String searchInternal(PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> searchResult) {
    if (searchResult.hasException()) {
      if (searchResult.hasSperrberechtigungFehltException()) {
        addMessageSperrberechtigungFehltException();
      } else {
        addMessagePersonenregisterSearchException(searchResult.getException());
      }

      return getViewId();
    }

    return super.searchInternal(searchResult);
  }

  @Override
  protected PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> querySearchResultByPersonendaten() {
    return getPersonenregister().search(getMandant(), UUID.randomUUID(), getSearchByPersonendatenSearchParameters());
  }

  @Override
  protected PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> querySearchResultByOrdnungsmerkmal() {
    return getPersonenregister().search(getMandant(), UUID.randomUUID(), getSearchByOrdnungsmerkmalSearchParameters());
  }

  @Override
  protected boolean isSearchResultEmpty(PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> searchResult) {
    return !searchResult.hasException() && !searchResult.hasPersonenregisterPersonendaten();
  }

  @Override
  protected boolean isSearchResultContainingUniqueSearchResultEntry(PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> searchResult) {
    if (!searchResult.hasException()) {
      List< ? extends PersonenregisterPersonendaten> personenregisterPersonendaten = searchResult.getPersonenregisterPersonendaten();

      if (personenregisterPersonendaten.size() == 1) {
        PersonenregisterPersonendaten personenregisterPerson = personenregisterPersonendaten.get(0);

        return personenregisterPerson != null && personenregisterPerson.getAnschriften().size() == 1;
      }
    }

    return false;
  }

  @Override
  protected PersonenregisterPersonendatenSearchResultEntry createUniqueSearchResultEntry(PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> searchResult) {
    PersonenregisterPersonendaten personenregisterPerson = searchResult.getPersonenregisterPersonendaten().get(0);

    return new PersonenregisterPersonendatenSearchResultEntry(personenregisterPerson, personenregisterPerson.getAnschriften());
  }

  @Override
  protected List<PersonenregisterPersonendatenSearchResultEntry> createSearchResultEntries(PersonenregisterSearchResult< ? extends PersonenregisterPersonendaten> searchResult) {
    List<PersonenregisterPersonendatenSearchResultEntry> result = new ArrayList<>();

    // NOTE, dass dem Suchergebnis für JEDE Person PRO Anschrift ein Eintrag SOWIE ZUSÄTZLICH ein Eintrag zum Erfassen einer Erreichbarkeitsanschrift hinzugefügt wird!
    for (PersonenregisterPersonendaten personenregisterPerson : searchResult.getPersonenregisterPersonendaten()) {
      if (personenregisterPerson != null) {
        List<PersonenregisterAnschrift> personenregisterAnschriften = personenregisterPerson.getAnschriften();

        boolean personenregisterErreichbarkeitsanschriftCreated = false;

        for (PersonenregisterAnschrift personenregisterAnschrift : personenregisterAnschriften) {
          personenregisterErreichbarkeitsanschriftCreated |= personenregisterAnschrift.hasAnschriftArt(AnschriftArt.ERREICHBARKEITSANSCHRIFT);

          result.add(new PersonenregisterPersonendatenSearchResultEntry(personenregisterPerson, asList(personenregisterAnschrift)));
        }

        if (isPersonenregisterErreichbarkeitsanschriftSupported() && !personenregisterErreichbarkeitsanschriftCreated) {
          result.add(new PersonenregisterPersonendatenSearchResultEntry(personenregisterPerson, asList(createPersonenregisterErreichbarkeitsanschrift())));
        }
      }
    }

    return result;
  }

  protected boolean isPersonenregisterErreichbarkeitsanschriftSupported() {
    return true;
  }

  private PersonenregisterAnschrift createPersonenregisterErreichbarkeitsanschrift() {
    return new PersonenregisterAnschrift(AnschriftArt.ERREICHBARKEITSANSCHRIFT);
  }

  @Override
  @Redirect
  public String create() {
    // NOTE, dass bereits als Suchparameter eingegebene Daten ggf. auf den nächsten View übernommen werden.
    return goToOnSelectionView(getSearchByPersonendatenSearchParameters());
  }

  @Override
  public String goToOnSelectionView(PersonenregisterPersonendatenSearchResultEntry personenregisterPersonendatenSearchResultEntry) {
    return goToOnSelectionView(personenregisterPersonendatenSearchResultEntry.toPersonenregisterPersonendaten());
  }

  private String goToOnSelectionView(PersonenregisterByPersonendatenSearchParameters searchParameters) {
    getOnSelectionViewTemplateSelect().setPersonenregisterByPersonendatenSearchParameters(searchParameters);

    return getOnSelectionViewId();
  }

  private String goToOnSelectionView(PersonenregisterPersonendaten personenregisterPersonendaten) {
    getOnSelectionViewTemplateSelect().setPersonenregisterPersonendaten(personenregisterPersonendaten);

    return getOnSelectionViewId();
  }

  @Override
  protected void addMessagePersonenregisterNichtErreichbar() {
    addWarningMessage("personenregister.nicht.erreichbar.failure.message", personenregisterMeta.getEinzahlMessage());
  }

  protected void addMessageSperrberechtigungFehltException() {
    addWarningMessage("personenregister.sperrberechtigung.fehlt.failure.message");
  }

  public String getGebietName() {
    return getCreateViewHelper().getSelectedGebietNameFormatted();
  }

  protected Mandant getMandant() {
    Wahltermin wahltermin = getWahltermin();

    if (wahltermin != null) {
      return wahltermin.getMandant();
    }

    return principal.getMandant();
  }
}
