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

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

import javax.inject.Inject;

import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.personenregister.boundary.AbstractPersonenregisterException;
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.PersonenregisterNichtErreichbarException;
import de.ivu.elect.business.personenregister.boundary.PersonenregisterSearchResult;
import de.ivu.elect.business.personenregister.entity.PersonenRegisterCacheRequest;
import de.ivu.elect.business.personenregister.entity.PersonenRegisterCacheResponse;
import de.ivu.elect.business.personenregister.entity.PersonenregisterMeta;
import de.ivu.elect.business.personenregister.entity.PersonenregisterPersonendaten;
import de.ivu.elect.business.system.control.AbstractMandantWebService;


import static org.apache.commons.lang3.StringUtils.isNotBlank;

public abstract class AbstractPersonenregisterWebService<T extends PersonenregisterPersonendaten> extends AbstractMandantWebService implements Personenregister<T> {

  private static final long serialVersionUID = 1L;

  private static final int DEFAULT_MAX_RESULT_SIZE = Integer.MAX_VALUE;

  @Inject
  private transient PersonenregisterMeta personenregisterMeta;

  @Inject
  private transient PersonenregisterCacheResolver personenregisterCacheResolver;

  public AbstractPersonenregisterWebService() {
    super();
  }

  @Override
  public boolean isAktiviert(Mandant mandant) {
    // ELECT-7511, naive Implementierung der Anforderung, ein Personenregister ein-/auszuschalten.
    return isNotBlank(getConfiguredServiceUrl(mandant));
  }

  @Override
  public String getName() {
    return personenregisterMeta.getEinzahlMessage();
  }

  @Override
  public String getKurzname() {
    return personenregisterMeta.getKurznameEinzahlMessage();
  }

  @Override
  public PersonenregisterSearchResult<T> search(Mandant mandant, UUID requestUUID, PersonenregisterByPersonendatenSearchParameters searchParameters) {
    return processSearch(mandant, requestUUID, searchParameters, () -> searchByPersonendaten(mandant, requestUUID, searchParameters));
  }

  @Override
  public void resetCache(Mandant mandant) {
    personenregisterCacheResolver.reset(mandant);
  }

  protected abstract PersonenregisterSearchResult<T> searchByPersonendaten(Mandant mandant, UUID requestUUID, PersonenregisterByPersonendatenSearchParameters searchParameters);

  @Override
  public PersonenregisterSearchResult<T> search(Mandant mandant, UUID requestUUID, PersonenregisterByOrdnungsmerkmalSearchParameters searchParameters) {
    return processSearch(mandant, requestUUID, searchParameters, () -> searchByOrdnungsmerkmal(mandant, requestUUID, searchParameters));
  }

  protected abstract PersonenregisterSearchResult<T> searchByOrdnungsmerkmal(Mandant mandant, UUID requestUUID, PersonenregisterByOrdnungsmerkmalSearchParameters searchParameters);

  private PersonenregisterSearchResult<T> processSearch(Mandant mandant, UUID requestUUID, AbstractPersonenregisterSearchParameters searchParameters, PersonenRegisterServiceCall personenRegisterServiceCall) {

    try {

      PersonenRegisterCache personenRegisterCache;

      if (!searchParameters.isPreventCacheLookup() && isUseCacheIfAvailable(mandant)) {
        personenRegisterCache = getPersonenregisterCacheResolver().getPersonenRegisterCache(mandant);
      } else {
        personenRegisterCache = new NoopPersonenRegisterCache();
      }

      PersonenRegisterCacheResponse personenRegisterCacheResponse = personenRegisterCache.lookupOrExecute(new PersonenRegisterCacheRequest(mandant, searchParameters), personenRegisterServiceCall);

      PersonenregisterSearchResult<?> searchResult = personenRegisterCacheResponse.getResponse();
      if (searchResult.hasPersonenregisterPersonendaten()) {
        List<PersonenregisterPersonendaten> personenregisterPersonendaten = new ArrayList<>(searchResult.getPersonenregisterPersonendaten());

        // ELECTDD-458
        if (isSearchByEinwohnerstatusSupported() && searchParameters.hasEinwohnerstatus()) {
          personenregisterPersonendaten.removeIf(personendaten -> !searchParameters.getEinwohnerstatus().equals(personendaten.getEinwohnerstatus()));
        }

        return new PersonenregisterSearchResult(requestUUID, searchResult.getRequestUUID(), personenregisterPersonendaten.subList(0,
            Math.min(personenregisterPersonendaten.size(), getDefaultMaxResultSize())), searchResult.getException());
      }

      return (PersonenregisterSearchResult<T>) searchResult;

    } catch (AbstractPersonenregisterException ex) {
      return new PersonenregisterSearchResult<>(requestUUID, ex);
    } catch (Exception unexpected) {
      warn("Unerwarteter Fehler beim Aufruf der Schnittstelle [mandant = " + mandant + ", requestUUID = " + requestUUID + "]", unexpected);
      throw new PersonenregisterNichtErreichbarException(unexpected);
    }

  }

  protected boolean isUseCacheIfAvailable(Mandant mandant) {
    return true;
  }

  protected int getDefaultMaxResultSize() {
    return DEFAULT_MAX_RESULT_SIZE;
  }

  protected PersonenregisterCacheResolver getPersonenregisterCacheResolver() {
    return personenregisterCacheResolver;
  }
}
