
package de.ivu.elect.presentation.entschaedigung;

import static java.util.Collections.emptyList;
import static org.apache.commons.lang3.StringUtils.defaultString;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.Instance;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.deltaspike.core.api.scope.ConversationGroup;
import org.apache.deltaspike.core.api.scope.ViewAccessScoped;

import de.ivu.elect.business.entschaedigung.boundary.EntschaedigungCalculator;
import de.ivu.elect.business.entschaedigung.boundary.EntschaedigungKriteriumEinsatzStatus;
import de.ivu.elect.business.entschaedigung.boundary.EntschaedigungKriteriumGesamtbetrag;
import de.ivu.elect.business.entschaedigung.boundary.EntschaedigungStatusParameters;
import de.ivu.elect.business.entschaedigung.control.EntschaedigungAggregator;
import de.ivu.elect.business.entschaedigung.control.EntschaedigungsartDefaultComparator;
import de.ivu.elect.business.entschaedigung.entity.AbstractEntschaedigungsart;
import de.ivu.elect.business.entschaedigung.entity.Entschaedigung;
import de.ivu.elect.business.entschaedigung.entity.Entschaedigungsart;
import de.ivu.elect.business.person.boundary.KontoverbindungFilterKriterium;
import de.ivu.elect.business.search.control.AbstractElectSearchParameters;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.wahl.boundary.WahlSelectChangeEvent;
import de.ivu.elect.business.wahl.boundary.WahlterminSelectChangeEvent;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.AbstractElectSearchView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.security.AbstractGebietSecurityFilter;

@OnlineHilfeId("563")
@ViewAccessScoped
@Named(EntschaedigungStatusView.COMPONENT_NAME)
@View(viewId = EntschaedigungStatusView.VIEW_ID, useCase = UseCasePermission.ENTSCHAEDIGUNG_SEARCH)
public class EntschaedigungStatusView extends AbstractElectSearchView<EntschaedigungStatusEntry> {

  private static final long serialVersionUID = 1L;

  public static final String VIEW_ID = "/entschaedigung/entschaedigung-status.xhtml";

  public static final String COMPONENT_NAME = "entschaedigungStatusView";

  @Inject
  private transient RequestHandlerEntschaedigungStatusView requestHandler;

  @Inject
  private transient EntschaedigungCalculator entschaedigungCalculator;

  @Inject
  @ConversationGroup(EntschaedigungStatusParameters.class)
  private transient EntschaedigungStatusParameters entschaedigungStatusParameters;

  public EntschaedigungStatusView() {
    super();
  }

  @Override
  public String getViewId() {
    return VIEW_ID;
  }

  @Nullable
  @PreConditionCheck
  public String checkPreCondition() {
    if (isWahlSelected() || isWahlterminSelected()) {
      return null;
    }

    return getStartViewId();
  }

  @Override
  public int getDefaultPageSize() {
    return getPageSizeShowAll();
  }

  @Override
  protected AbstractGebietSecurityFilter<EntschaedigungStatusEntry> getGebietSecurityFilter() {
    return KEIN_GEBIET_SECURITY_FILTER;
  }

  @Override
  public AbstractElectSearchParameters getSearchParameters() {
    return entschaedigungStatusParameters;
  }

  /**
   * @deprecated To be removed...
   * @see EntschaedigungStatusView#initSearchParametersByDefaults
   */
  @Deprecated
  @Override
  public void presetSearchParameters() {
    // To be removed...
  }

  @Override
  protected void initSearchParametersByDefaults() {
    super.initSearchParametersByDefaults();

    entschaedigungStatusParameters.setEntschaedigungKriteriumGesamtbetrag(EntschaedigungKriteriumGesamtbetrag.NUR_GROESSER_NULL); // ELECT-9124
  }

  @Override
  public void addProperties(FormBuilder formBuilder) {
    // Cf. EntschaedigungInAuszahlungBulkCreateView#addProperties!!!
    formBuilder.addProperty(entschaedigungStatusParameters.getEntschaedigungsartenProperty(getSupportedEntschaedigungsarten()));
    formBuilder.addProperty(entschaedigungStatusParameters.getEntschaedigungKriteriumEinsatzStatusProperty());
    formBuilder.addProperty(entschaedigungStatusParameters.getPersonendatenKontoverbindungFilterKriteriumProperty());

    formBuilder.addProperty(entschaedigungStatusParameters.getEntschaedigungKriteriumGesamtbetragProperty());
  }

  @Override
  public List<EntschaedigungStatusEntry> getSearchResult() {
    requestHandler.nothing(); // ELECT-248

    if (entschaedigungStatusParameters.hasEntschaedigungsarten()) { // Initial NICHT suchen!
      return super.getSearchResult();
    }

    return emptyList();
  }

  @Override
  protected List<EntschaedigungStatusEntry> performSearch() {
    List<EntschaedigungStatusEntry> result = new ArrayList<>();

    long entschaedigungStatusEntryIndex = 1;

    BigDecimal summeGesamtbetragPerEntschaedigungsart = BigDecimal.ZERO;
    String summeGesamtbetragPerEntschaedigungsartWaehrung = "";

    for (Entry<Entschaedigungsart, List<Entschaedigung>> entry : getEntschaedigungenPerEntschaedigungsartSorted().entrySet()) {
      Entschaedigungsart entschaedigungsart = entry.getKey();
      List<Entschaedigung> entschaedigungen = entry.getValue();

      String entschaedigungsartWaehrung = entschaedigungsart.getWaehrungAlsIsoCode();
      String entschaedigungsartEinzelbetragMitWaehrung = entschaedigungsart.getEinzelbetragInitialMitWaehrung(getUserLocale().getLocale());

      if (entschaedigungsart.isEinzelbetragVariabel()) {
        entschaedigungsartEinzelbetragMitWaehrung = getMessage(AbstractEntschaedigungsart.MESSAGE_KEY_EINZELBETRAG_EINZAHL_VARIABEL);
      }

      Long entschaedigungsartAnzahlGewaehrt = Long.valueOf(entschaedigungen.size());
      BigDecimal entschaedigungsartGesamtbetrag = new EntschaedigungAggregator(entschaedigungen).calculateGesamtbetrag();

      result.add(new EntschaedigungStatusEntry(entschaedigungStatusEntryIndex++, entschaedigungsart.getBezeichnung(), entschaedigungsart.getBeschreibung(), entschaedigungsartEinzelbetragMitWaehrung,
          entschaedigungsartAnzahlGewaehrt, formatBetrag(entschaedigungsartGesamtbetrag, entschaedigungsartWaehrung)));

      summeGesamtbetragPerEntschaedigungsart = summeGesamtbetragPerEntschaedigungsart.add(entschaedigungsartGesamtbetrag);
      summeGesamtbetragPerEntschaedigungsartWaehrung = defaultString(entschaedigungsartWaehrung, summeGesamtbetragPerEntschaedigungsartWaehrung);
    }

    if (!result.isEmpty()) {
      result.add(new EntschaedigungStatusEntry(entschaedigungStatusEntryIndex + 1, getMessage("entity.einzahl.ENTSCHAEDIGUNG_STATUS_ENTRY.GESAMTBETRAG.SUMME"), "",
          formatBetrag(summeGesamtbetragPerEntschaedigungsart, summeGesamtbetragPerEntschaedigungsartWaehrung)));
    }

    return result;
  }

  private Map<Entschaedigungsart, List<Entschaedigung>> getEntschaedigungenPerEntschaedigungsartSorted() {
    entschaedigungStatusParameters.setWahl(getSelectedWahl());
    entschaedigungStatusParameters.setWahltermin(getSelectedWahltermin());

    if (!entschaedigungStatusParameters.hasKontoverbindungFilterKriterium()) {
      entschaedigungStatusParameters.setKontoverbindungFilterKriterium(KontoverbindungFilterKriterium.ALLE);
    }

    if (!entschaedigungStatusParameters.hasEntschaedigungKriteriumEinsatzStatus()) {
      entschaedigungStatusParameters.setEntschaedigungKriteriumEinsatzStatus(EntschaedigungKriteriumEinsatzStatus.AUSZAHLBAR_ODER_BERECHENBAR);
    }

    if (!entschaedigungStatusParameters.hasEntschaedigungKriteriumGesamtbetrag()) {
      entschaedigungStatusParameters.setEntschaedigungKriteriumGesamtbetrag(EntschaedigungKriteriumGesamtbetrag.ALLE);
    }

    Collection<Entschaedigung> entschaedigungenCalculated = entschaedigungCalculator.calculateEntschaedigungen(entschaedigungStatusParameters);

    TreeMap<Entschaedigungsart, List<Entschaedigung>> result = new TreeMap<>(new EntschaedigungsartDefaultComparator());
    result.putAll(new EntschaedigungAggregator(entschaedigungenCalculated).groupByEntschaedigungsart());

    if (entschaedigungStatusParameters.getEntschaedigungKriteriumGesamtbetrag().isGesamtbetragZeroIncluded()) {
      // Immer Einträge für alle ausgewählten Entschädigungsarten anzeigen! Selbst, wenn für diese keine Entschädigungen berechnet werden konnten...
      for (Entschaedigungsart entschaedigungsart : entschaedigungStatusParameters.getEntschaedigungsarten()) {
        result.putIfAbsent(entschaedigungsart, emptyList());
      }
    }

    return result;
  }

  public boolean isBtnCalculateRendered() {
    return true;
  }

  public boolean isBtnCalculateDisabled() {
    return false;
  }

  @Redirect
  public String calculate() {
    return search();
  }

  private List<Entschaedigungsart> getSupportedEntschaedigungsarten() {
    return new ArrayList<>(entschaedigungCalculator.getSupportedEntschaedigungsarten(getSelectedWahl(), getSelectedWahltermin()));
  }

  public void onWahlSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlSelectChangeEvent event) {
    reset();
  }

  public void onWahlterminSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) WahlterminSelectChangeEvent event) {
    reset();
  }

  private String formatBetrag(BigDecimal betrag, String waehrungAlsIsoCode) {
    return AbstractEntschaedigungsart.formatBetragMitWaehrung(getUserLocale().getLocale(), betrag, waehrungAlsIsoCode);
  }

  @RequestScoped
  public static class RequestHandlerEntschaedigungStatusView {

    @View
    @Inject
    private transient Instance<EntschaedigungStatusView> statusViews;

    @PostConstruct
    public void init() {
      if (!FacesContext.getCurrentInstance().isPostback()) {
        for (EntschaedigungStatusView searchView : statusViews) {
          searchView.resetSearchResult();
        }
      }
    }

    public void nothing() {
      // Do nothing.
    }
  }
}
