
package de.ivu.elect.presentation.wahlbeteiligung;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.inject.Inject;
import javax.inject.Named;

import de.ivu.elect.business.ergebnis.boundary.ErgebnisSessionSelect;
import de.ivu.elect.business.gebietsbaum.boundary.GebietNichtAusgewaehltException;
import de.ivu.elect.business.gebietsbaum.boundary.GebietSelectChangeEvent;
import de.ivu.elect.business.gebietsbaum.boundary.WahldurchfuehrungsgebietSessionSelect;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.UseCaseSecure;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelector;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungAuswertungExtendedDAO;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungEdit;
import de.ivu.elect.business.wahlbeteiligung.boundary.WahlbeteiligungExtendedDAO;
import de.ivu.elect.business.wahlbeteiligung.entity.Wahlbeteiligung;
import de.ivu.elect.business.wahlbeteiligung.entity.WahlbeteiligungAuswertung;
import de.ivu.elect.jee.faces.FacesContextUtil;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.faces.UserLocale;
import de.ivu.elect.jee.presentation.dynamicform.AdvancedDynaFormModel;
import de.ivu.elect.jee.presentation.dynamicform.DynamicFormEditOrCreateView;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.jee.presentation.security.StartViewResolver;
import de.ivu.elect.jee.presentation.view.AbstractView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.gebietsbaum.tree.AbstractNoStoreGebietsbaumView;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;
import de.ivu.elect.presentation.replikation.ReplikationPresentationStatus;
import de.ivu.elect.presentation.security.GebietSecurity;
import de.ivu.elect.presentation.stimmeingabe.StimmeingabeErfassenViewResolver;
import de.ivu.elect.presentation.wahlbeteiligung.form.WahlbeteiligungViewFormBuilder;

/**
 * !!!!!!!!!!!!!!!!!! Diese Klasse keinesfalls als Vorlage für weitere Funktionen verwenden !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
@RequestScoped
@OnlineHilfeId("703")
@Named(GebietWahlbeteiligungEditView.COMPONENT_NAME)
@View(viewId = GebietWahlbeteiligungEditView.VIEW_ID, useCase = UseCasePermission.GEBIET_WAHLBETEILIGUNG_EDIT)
@UseCaseSecure(UseCasePermission.GEBIET_WAHLBETEILIGUNG_EDIT)
public class GebietWahlbeteiligungEditView extends AbstractView implements Serializable, DynamicFormEditOrCreateView {

  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "gebietWahlbeteiligungEditView";

  public static final String VIEW_ID = "/wahlbeteiligung/gebiet-wahlbeteiligung-edit.xhtml";

  @Inject
  private ElectCommonPrincipal principal;

  @Inject
  protected transient WahldurchfuehrungsgebietSessionSelect gebietSelect;

  @Inject
  private transient WahlSessionSelector wahlSessionSelector;

  @Inject
  private transient StartViewResolver startViewResolver;

  @Inject
  private transient WahlbeteiligungAuswertungExtendedDAO wahlbeteiligungAuswertungDAO;

  @Inject
  private transient WahlbeteiligungExtendedDAO wahlbeteiligungDAO;

  @Inject
  private transient WahlbeteiligungViewFormBuilder wahlbeteiligungViewFormBuilder;

  @Inject
  private transient WahlbeteiligungEdit wahlbeteiligungEdit;

  @Inject
  private transient AbstractNoStoreGebietsbaumView gebietsbaumView;

  @Inject
  private transient StimmeingabeErfassenViewResolver stimmeingabeErfassenViewResolver;

  @Inject
  private transient UserLocale userLocale;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient GebietSecurity gebietSecurity;

  @Inject
  private transient ReplikationPresentationStatus replikationPresentationStatus;

  @Inject
  private transient ErgebnisSessionSelect ergebnisSessionSelect;

  private AdvancedDynaFormModel model;

  private Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertung;

  public GebietWahlbeteiligungEditView() {
    super();
  }

  @PreConditionCheck
  public String checkPreCondition() {
    if (!isWahlSelected()) {
      return getStartViewId();
    }

    if (isGebietSelected()) {
      if (!stimmeingabeErfassenViewResolver.isErfassungsgebietSelected()) {
        return WahlbeteiligungAuswertungStatusuebersichtView.VIEW_ID;
      }

      return null;
    }

    throw new GebietNichtAusgewaehltException();
  }

  public void preRenderView() {
    if (isAuswertungsgebietSelected() && !areWahlberechtigteVorhanden()) {
      addWarningMessage("gebiet.wahlbeteiligung.edit.anzahl.wahlberechtigter.nicht.hinterlegt.message");
    }

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedWahldurchfuehrungsgebiet();

    if (!hasGebietPermission(wahldurchfuehrungsgebiet)) {
      addWarningMessage("security.permission.missing.for.gebiet", gebietNummerFormatter.getNameMitNummer(wahldurchfuehrungsgebiet));
    }
  }

  private boolean hasGebietPermission(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return gebietSecurity.getBearbeitenGebietSecurity().isRechtVorhanden(wahldurchfuehrungsgebiet);
  }

  protected String getStartViewId() {
    return startViewResolver.getStartViewId();
  }

  private boolean isWahlSelected() {
    return wahlSessionSelector.isSelected();
  }

  private Wahl getSelectedWahl() {
    return wahlSessionSelector.getEntity();
  }

  protected boolean isGebietSelected() {
    return gebietSelect.isSelected();
  }

  private Wahldurchfuehrungsgebiet getSelectedWahldurchfuehrungsgebiet() {
    return gebietSelect.getEntity();
  }

  private boolean areWahlberechtigteVorhanden() {
    return getSelectedWahldurchfuehrungsgebiet().hasAnzahlWahlberechtigteOhneWahlschein();
  }

  public boolean isFooterRendered() {
    return hasGebietPermission(getSelectedWahldurchfuehrungsgebiet()) //
        && !isSlave() //
        && isAuswertungsgebietSelected() //
        //  Soll auch ohne A-Werte gehen && areWahlberechtigteVorhanden() //
        && !getWahlbeteiligungProAuswertung().isEmpty();
  }

  private boolean isAuswertungsgebietSelected() {
    return !getSelectedWahldurchfuehrungsgebiet().isBriefwahlgebiet();
  }

  public String getPageHeader() {
    return getMessage("gebiet.wahlbeteiligung.edit.page.header", gebietNummerFormatter.getNameMitNummer(getSelectedWahldurchfuehrungsgebiet()));
  }

  public String getFarbe() {
    return "#" + getSelectedWahl().getFarbe();
  }

  @Override
  public boolean isBtnSaveRendered() {
    return hasPermission(UseCasePermission.GEBIET_WAHLBETEILIGUNG_EDIT);
  }

  @Override
  public String getBtnSaveLabel() {
    return getMessage("entity.edit.action.save");
  }

  @Override
  @Redirect
  @UseCaseSecure(UseCasePermission.GEBIET_WAHLBETEILIGUNG_EDIT)
  public String save() {
    if (validiereUndZeigeFehlerAn()) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedWahldurchfuehrungsgebiet();

      wahlbeteiligungEdit.saveErfassungenFuerGebiet(principal.getBenutzerKennung(), wahldurchfuehrungsgebiet, getWahlbeteiligungProAuswertung(), ergebnisSessionSelect.getEntity());

      addInfoMessage("gebiet.wahlbeteiligung.edit.save.success.message");
    }

    return FacesContextUtil.getCurrentViewId();
  }

  private boolean validiereUndZeigeFehlerAn() {

    Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertung = getWahlbeteiligungProAuswertung();

    List<WahlbeteiligungAuswertung> auswertungen = new ArrayList<>(wahlbeteiligungProAuswertung.keySet());

    auswertungen.sort(Comparator.comparing(WahlbeteiligungAuswertung::getDatum));

    boolean result = true;

    for (WahlbeteiligungAuswertung auswertung : auswertungen) {
      Wahlbeteiligung beteiligung = wahlbeteiligungProAuswertung.get(auswertung);

      if (nonNull(beteiligung.getAnzahlWaehlerGesamt()) && beteiligung.getAnzahlWaehlerGesamt() == 0L) {
        addWarningMessage("gebiet.wahlbeteiligung.edit.wahlbeteiligung.gleich.null", //
            userLocale.formatDate(auswertung.getDatum()), //
            userLocale.formatTimeHHmm(auswertung.getDatum()));
      }
    }

    for (int ii = 0; ii < auswertungen.size() - 1; ii++) {
      for (int jj = ii + 1; jj < auswertungen.size(); jj++) {
        WahlbeteiligungAuswertung auswertungFrueher = auswertungen.get(ii);
        WahlbeteiligungAuswertung auswertungSpaeter = auswertungen.get(jj);
        Wahlbeteiligung beteiligungFrueher = wahlbeteiligungProAuswertung.get(auswertungFrueher);
        Wahlbeteiligung beteiligungSpaeter = wahlbeteiligungProAuswertung.get(auswertungSpaeter);

        if (nonNull(beteiligungFrueher.getAnzahlWaehlerGesamt()) //
            && nonNull(beteiligungSpaeter.getAnzahlWaehlerGesamt()) //
            && beteiligungFrueher.getAnzahlWaehlerGesamt() > beteiligungSpaeter.getAnzahlWaehlerGesamt()) {
          addErrorMessage("gebiet.wahlbeteiligung.edit.wahlbeteiligung.frueher.groesser.als.wahlbeteiligung.spaeter", userLocale.formatDate(auswertungFrueher.getDatum()), //
              userLocale.formatTimeHHmm(auswertungFrueher.getDatum()), userLocale.formatDate(auswertungSpaeter.getDatum()), userLocale.formatTimeHHmm(auswertungSpaeter.getDatum()));

          result = false;
        }
      }
    }

    return result;
  }

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

  @Override
  @Redirect
  public String cancel() {
    return null;
  }

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

  @Override
  @Redirect
  public String reset() {
    resetModel();
    return VIEW_ID;
  }

  private boolean hasPermission(UseCasePermission permission) {
    return principal.hasUseCasePermission(permission);
  }

  @Override
  public AdvancedDynaFormModel getModel() {
    if (model == null) {
      FormBuilder formBuilder = new FormBuilder();

      if (!isAuswertungsgebietSelected()) {
        formBuilder.addSeparator("wahlbeteiligung.separator.kein.auswertungsgebiet");
      } else {
        Map<WahlbeteiligungAuswertung, Wahlbeteiligung> wahlbeteiligungProAuswertung = getWahlbeteiligungProAuswertung();
        List<WahlbeteiligungAuswertung> auswertungen = new ArrayList<>(wahlbeteiligungProAuswertung.keySet());

        if (auswertungen.isEmpty()) {
          formBuilder.addSeparator("wahlbeteiligung.separator.keine.auswertungen");
        } else {
          auswertungen.sort((one, two) -> one.getDatum().compareTo(two.getDatum()));

          Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedWahldurchfuehrungsgebiet();

          wahlbeteiligungViewFormBuilder.addProperties(formBuilder, wahldurchfuehrungsgebiet, auswertungen, wahlbeteiligungProAuswertung);
        }
      }

      model = formBuilder.createModel();
    }

    return model;
  }

  private Map<WahlbeteiligungAuswertung, Wahlbeteiligung> getWahlbeteiligungProAuswertung() {
    if (isNull(wahlbeteiligungProAuswertung)) {
      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = getSelectedWahldurchfuehrungsgebiet();

      List<WahlbeteiligungAuswertung> wahlbeteiligungAuswertungen = wahlbeteiligungAuswertungDAO.findByWahl(getSelectedWahl());

      List<Wahlbeteiligung> wahlbeteiligungen = wahlbeteiligungDAO.findByWahldurchfuehrungsgebiet(wahldurchfuehrungsgebiet);

      wahlbeteiligungProAuswertung = new HashMap<>();

      for (Wahlbeteiligung wahlbeteiligung : wahlbeteiligungen) {
        wahlbeteiligungProAuswertung.put(wahlbeteiligung.getWahlbeteiligungAuswertung(), wahlbeteiligung);
      }

      for (WahlbeteiligungAuswertung auswertung : wahlbeteiligungAuswertungen) {
        if (!wahlbeteiligungProAuswertung.containsKey(auswertung)) {
          Wahlbeteiligung wahlbeteilingung = new Wahlbeteiligung();
          wahlbeteilingung.setWahldurchfuehrungsgebiet(wahldurchfuehrungsgebiet);
          wahlbeteilingung.setWahlbeteiligungAuswertung(auswertung);
          wahlbeteilingung.setAnzahlWaehlerMitWahlschein(0L);
          wahlbeteiligungProAuswertung.put(auswertung, wahlbeteilingung);
        }
      }
    }

    return wahlbeteiligungProAuswertung;
  }

  public void onGebietSelectChangeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) GebietSelectChangeEvent event) {
    reset();

    Gebiet neueEntity = event.getNeueEntity();

    if (neueEntity != null) { // ELECTDD-404 Abmeldung
      // Wenn Erfassungsgebiet => alles neu laden
      String targetViewId = stimmeingabeErfassenViewResolver.getWahlbeteiligungErfassenViewId();

      if (!VIEW_ID.equals(targetViewId)) {
        gebietsbaumView.reset();

        FacesContextUtil.redirectToView(targetViewId);
      }
    }
  }

  private void resetModel() {
    model = null;
    wahlbeteiligungProAuswertung = null;
  }

  private boolean isSlave() {
    return replikationPresentationStatus.isSlave();
  }
}
