package de.ivu.elect.presentation.gebietsbaum;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Nullable;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.boundary.CoreGebietPruefkennzeichenGenerieren;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietPK;
import de.ivu.elect.business.gebietsbaum.entity.GebietPruefkennzeichenGenerierenArt;
import de.ivu.elect.business.gebietsbaum.entity.GebietPruefkennzeichenGenerierenEindeutigkeit;
import de.ivu.elect.business.gebietsbaum.entity.GebietPruefkennzeichenGenerierenOptions;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.jee.entity.customdata.EmptyCustomData;
import de.ivu.elect.jee.faces.PreConditionCheck;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.LambdaBooleanProperty;
import de.ivu.elect.jee.presentation.dynamicform.LambdaEnumProperty;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.AbstractElectEditView;
import de.ivu.elect.presentation.View;
import de.ivu.elect.presentation.security.BearbeitenGebietSecurity;


import static java.util.Arrays.asList;

@RequestScoped
@Named(GebietPruefkennzeichenGenerierenView.COMPONENT_NAME)
@View(viewId = GebietPruefkennzeichenGenerierenView.VIEW_ID, useCase = UseCasePermission.GEBIET_PRUEFKENNZEICHEN_GENERIEREN)
public class GebietPruefkennzeichenGenerierenView extends AbstractElectEditView<Gebiet> {
  private static final long serialVersionUID = 1L;

  public static final String COMPONENT_NAME = "gebietPruefkennzeichenGenerierenView";

  public static final String VIEW_ID = "/gebietsbaum/gebiet-pruefkennzeichen-generieren.xhtml";

  public static final String PARENT_VIEW_ID = "/gebietsbaum/gebiet-search.xhtml";

  @Inject
  private transient CoreGebietPruefkennzeichenGenerieren gebietPruefkennzeichenGenerieren;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient BearbeitenGebietSecurity bearbeitenGebietSecurity;

  private GebietPruefkennzeichenGenerierenArt gebietPruefkennzeichenGenerierenArt = GebietPruefkennzeichenGenerierenArt.ALPHANUMERISCH_ACHTSTELLIG;

  private GebietPruefkennzeichenGenerierenEindeutigkeit gebietPruefkennzeichenGenerierenEindeutigkeit = GebietPruefkennzeichenGenerierenEindeutigkeit.WAHLUEBERGREIFEND_EINDEUTIG;

  private Set<WahlPK> wahlen = null;

  boolean bestehendeKennzeichenUeberschreiben  = false;

  @Nullable
  @PreConditionCheck
  public String checkPreCondition() {
    if (getSelectedWahl() == null) {
      return getStartViewId();
    }

    return null;
  }

  @Override
  protected Gebiet getEntity() {
    return new Gebiet(EmptyCustomData.INSTANCE);
  }

  @Override
  protected void addProperties(FormBuilder formBuilder, Gebiet entity) {
    List<Wahl> wahlenSortiertNachPosition = getSelectedWahl().getWahltermin().getWahlenSortiertNachPosition().stream()
        .filter(w -> w.isRechtZumEinsehenVorhanden(getPrincipal())).collect(Collectors.toList());

    formBuilder.addSeparator("gebiet.pruefkennzeichen.generieren.wahlen");

    for (Wahl wahl : wahlenSortiertNachPosition) {
      formBuilder.addProperty(new LambdaBooleanProperty(getMessage("gebiet.pruefkennzeichen.generieren.wahl", wahl.getName()), false, b -> {
        if (b) {
          getWahlen().add(wahl.getPk());
        } else {
          getWahlen().remove(wahl.getPk());
        }
      }, () -> getWahlen().contains(wahl.getPk()))).withReadonly(wahl.equals(getSelectedWahl()));
    }

    formBuilder.addSeparator("gebiet.pruefkennzeichen.generieren.einstellung");

    formBuilder.addProperty(new LambdaEnumProperty<>(getMessage("gebiet.pruefkennzeichen.art"), true, this::setGebietPruefkennzeichenGenerierenArt,
        this::getGebietPruefkennzeichenGenerierenArt,
        GebietPruefkennzeichenGenerierenArt.class, asList(GebietPruefkennzeichenGenerierenArt.values())));

    formBuilder.addProperty(
        new LambdaEnumProperty<>(getMessage("gebiet.pruefkennzeichen.eindeutigkeit"), true, this::setGebietPruefkennzeichenGenerierenEindeutigkeit,
            this::getGebietPruefkennzeichenGenerierenEindeutigkeit,
            GebietPruefkennzeichenGenerierenEindeutigkeit.class, asList(GebietPruefkennzeichenGenerierenEindeutigkeit.values())));


    if (isPruefkennzeichenVorhanden(wahlenSortiertNachPosition)) {
      formBuilder.addSeparator("gebiet.pruefkennzeichen.generieren.hinweis.separator");
      formBuilder.addProperty(new MessageProperty(getMessage("gebiet.pruefkennzeichen.generieren.hinweis.message")));
      formBuilder.addProperty(
          new LambdaBooleanProperty(getMessage("gebiet.pruefkennzeichen.bestehende.ueberschreiben"), false, this::setBestehendeKennzeichenUeberschreiben,
              this::isBestehendeKennzeichenUeberschreiben)).withReadonly(!hasPermission(UseCasePermission.GEBIET_EXTERNE_ID_UEBERSCHREIBEN));
    }

  }

  private boolean isPruefkennzeichenVorhanden(List<Wahl> wahlenSortiertNachPosition) {
    return wahlenSortiertNachPosition.stream().flatMap(wahl -> gebietExtendedDAO.findByWahl(wahl).stream())
        .anyMatch(gebiet -> StringUtils.isNotBlank(gebiet.getExterneId()));
  }

  @Override
  public String delete() {
    throw new UnsupportedOperationException();
  }

  @Override
  protected boolean isGebietBearbeitenRechtPruefen() {
    return false;
  }

  @Override
  protected boolean isSavePermissionVorhanden() {
    return hasPermission(UseCasePermission.GEBIET_PRUEFKENNZEICHEN_GENERIEREN);
  }

  @Override
  @Redirect
  public String save() {
    Long gebieteVeraendert = gebietPruefkennzeichenGenerieren.generierePruefkennzeichenFuerErfassungsgebiete(createOptions());

    if (gebieteVeraendert > 0) {
      addInfoMessage("gebiet.pruefkennzeichen.generieren.gebiete.veraendert", gebieteVeraendert);
    } else {
      addWarningMessage(
          "gebiet.pruefkennzeichen.generieren.kein.gebiet.veraendert");
    }

    return VIEW_ID;
  }

  private GebietPruefkennzeichenGenerierenOptions createOptions() {
    Set<GebietPK> gebieteMitSchreibrechten = getWahlen().stream()
        .flatMap(wahlPK -> gebietExtendedDAO.findByWahl(wahlExtendedDAO.findByPrimaryKey(wahlPK)).stream())
        .filter(gebiet -> bearbeitenGebietSecurity.isRechtVorhanden(gebiet)).map(Gebiet::getPk).collect(Collectors.toSet());

    return new GebietPruefkennzeichenGenerierenOptions(getWahlen(), getGebietPruefkennzeichenGenerierenArt(),
        getGebietPruefkennzeichenGenerierenEindeutigkeit(),
        gebieteMitSchreibrechten, isBestehendeKennzeichenUeberschreiben());
  }

  @Override
  public String getBtnSaveLabel() {
    return getMessage("gebiet.pruefkennzeichen.generieren.label");
  }

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

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

  public GebietPruefkennzeichenGenerierenArt getGebietPruefkennzeichenGenerierenArt() {
    return gebietPruefkennzeichenGenerierenArt;
  }

  public void setGebietPruefkennzeichenGenerierenArt(GebietPruefkennzeichenGenerierenArt gebietPruefkennzeichenGenerierenArt) {
    this.gebietPruefkennzeichenGenerierenArt = gebietPruefkennzeichenGenerierenArt;
  }

  public GebietPruefkennzeichenGenerierenEindeutigkeit getGebietPruefkennzeichenGenerierenEindeutigkeit() {
    return gebietPruefkennzeichenGenerierenEindeutigkeit;
  }

  public void setGebietPruefkennzeichenGenerierenEindeutigkeit(GebietPruefkennzeichenGenerierenEindeutigkeit gebietPruefkennzeichenGenerierenEindeutigkeit) {
    this.gebietPruefkennzeichenGenerierenEindeutigkeit = gebietPruefkennzeichenGenerierenEindeutigkeit;
  }

  public Set<WahlPK> getWahlen() {
    if (wahlen == null) {
      wahlen = new HashSet<>();
      wahlen.add(getSelectedWahl().getPk());
    }
    return wahlen;

  }

  public boolean isBestehendeKennzeichenUeberschreiben() {
    return bestehendeKennzeichenUeberschreiben;
  }

  public void setBestehendeKennzeichenUeberschreiben(boolean bestehendeKennzeichenUeberschreiben) {
    this.bestehendeKennzeichenUeberschreiben = bestehendeKennzeichenUeberschreiben;
  }
}
