
package de.ivu.elect.presentation.stimmeingabe;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;

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

import de.ivu.elect.business.gruppe.entity.GruppeMeta;
import de.ivu.elect.business.gruppeimgebiet.entity.GruppeImGebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmeingabe.boundary.StimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmen;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenMeta;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeGruppeStimmenTableLayout;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabeStimmen;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehler;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehlerart;
import de.ivu.elect.business.stimmen.boundary.GruppeStimmeLabelGenerator;
import de.ivu.elect.business.stimmen.boundary.StimmenartLabelGenerator;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.presentation.redirect.Redirect;
import de.ivu.elect.presentation.hilfe.OnlineHilfeId;

@OnlineHilfeId(value = "342", mehrfachVergebenOk = true)
public abstract class AbstractStimmeingabeGruppeStimmenErfassenView extends AbstractStimmeingabeKopfzahlErfassenView
    implements
      StimmeingabeErfassenViewHavingStimmeingabenSystemGruppeStimmenAndStimmeingabenGruppeStimmen {

  private static final long serialVersionUID = 1L;

  public static final String MESSAGE_KEY_FEHLER_AUF_UNTERSEITE = "stimmeingabe.gruppe.stimmen.erfassen.fehler.auf.unterseite";

  @Inject
  protected transient GruppeMeta gruppeMeta;

  @Inject
  protected transient StimmeingabeGruppeStimmenMeta stimmeingabeGruppeStimmenMeta;

  @Inject
  @ConversationGroup(StimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue.class)
  protected transient StimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue stimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue;

  @Inject
  protected transient StimmenartLabelGenerator stimmenartLabelGenerator;

  @Inject
  protected transient GruppeStimmeLabelGenerator gruppeStimmeLabelGenerator;

  public AbstractStimmeingabeGruppeStimmenErfassenView() {
    super();
  }

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

  protected abstract boolean isStimmeingabenGruppeStimmenDirektstimmeRendered();

  public boolean isStimmeingabenGruppeStimmenDirektstimmeGruppeBezeichnungRendered() {
    return getStimmeingabeGruppeStimmenErfassenLabelGenerator().isGruppeStimmenStimmeingabeGruppeBezeichnungRendered();
  }

  public String getStimmeingabenGruppeStimmenDirektstimmeGruppeBezeichnung() {
    return gruppeMeta.getEinzahlMessage();
  }

  @Override
  public String getStimmeingabenGruppeStimmenDirektstimmeKennung() {
    return gruppeStimmeLabelGenerator.getDirektstimmeLabelGenerator().formatDirektstimmeAnzahlGueltigeGesamtKennung(getGebiet());
  }

  @Override
  public String getStimmeingabenGruppeStimmenDirektstimmeBezeichnung() {
    return stimmenartLabelGenerator.getDirektstimmeBezeichnungPlural(getGebiet());
  }

  protected boolean isStimmeingabenGruppeStimmenDirektstimmeVergleichRendered() {
    return isVergleichErgebniseingangVorhanden();
  }

  protected String getStimmeingabenGruppeStimmenDirektstimmeVergleichBezeichnung() {
    return getMessage("stimmeingabe.erfassen.vergleich.ergebniseingang.bezeichnung");
  }

  protected abstract boolean isStimmeingabenGruppeStimmenListenstimmeRendered();

  public boolean isStimmeingabenGruppeStimmenListenstimmeGruppeBezeichnungRendered() {
    return getStimmeingabeGruppeStimmenErfassenLabelGenerator().isGruppeStimmenStimmeingabeGruppeBezeichnungRendered();
  }

  public String getStimmeingabenGruppeStimmenListenstimmeGruppeBezeichnung() {
    return gruppeMeta.getEinzahlMessage();
  }

  @Override
  public String getStimmeingabenGruppeStimmenListenstimmeKennung() {
    return gruppeStimmeLabelGenerator.getListenstimmeLabelGenerator().formatListenstimmeAnzahlGueltigeGesamtKennung(getGebiet());
  }

  @Override
  public String getStimmeingabenGruppeStimmenListenstimmeBezeichnung() {
    return stimmenartLabelGenerator.getListenstimmeBezeichnungPlural(getGebiet());
  }

  protected boolean isStimmeingabenGruppeStimmenListenstimmeVergleichRendered() {
    return isVergleichErgebniseingangVorhanden();
  }

  protected String getStimmeingabenGruppeStimmenListenstimmeVergleichBezeichnung() {
    return getMessage("stimmeingabe.erfassen.vergleich.ergebniseingang.bezeichnung");
  }

  @Override
  public StimmeingabeGruppeStimmenTableLayout getStimmeingabenSystemGruppeStimmenTableLayout() {
    return new StimmeingabeGruppeStimmenTableLayout( //
        isErfassungGesperrt(), //

        getStimmeingabeErfassenUmgangKennungDefault(), //
        stimmeingabeGruppeStimmenMeta.getKennungEinzahlMessage(), //
        stimmeingabeGruppeStimmenMeta.getKennungEinzahlMessage(), //

        true, //
        stimmeingabeGruppeStimmenMeta.getBezeichnungEinzahlMessage(), //

        isStimmeingabenGruppeStimmenDirektstimmeRendered(), //
        getStimmeingabenGruppeStimmenDirektstimmeBezeichnung(), //

        isStimmeingabenGruppeStimmenDirektstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenDirektstimmeVergleichBezeichnung(), //

        isStimmeingabenGruppeStimmenListenstimmeRendered(), //
        getStimmeingabenGruppeStimmenListenstimmeBezeichnung(), //

        isStimmeingabenGruppeStimmenListenstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenListenstimmeVergleichBezeichnung() //
    );
  }

  @Override
  public List<StimmeingabeGruppeStimmen> getStimmeingabenSystemGruppeStimmen() {
    return getStimmeingabeGruppeStimmenErfassenModelBuilder().layoutStimmeingabenSystemGruppeStimmen(getStimmeingabeGruppeStimmenErfassenModel(),
        createStimmeingabeGruppeStimmenErfassenModelContext());
  }

  @Override
  public StimmeingabeGruppeStimmenTableLayout getStimmeingabenGruppeStimmenTableLayout() {
    return new StimmeingabeGruppeStimmenTableLayout( //
        isErfassungGesperrt(), //

        getStimmeingabeErfassenUmgangKennungDefault(), //
        stimmeingabeGruppeStimmenMeta.getKennungEinzahlMessage(), //
        stimmeingabeGruppeStimmenMeta.getKennungEinzahlMessage(), //

        true, //
        stimmeingabeGruppeStimmenMeta.getBezeichnungEinzahlMessage(), //

        isStimmeingabenGruppeStimmenDirektstimmeRendered(), //
        getStimmeingabenGruppeStimmenDirektstimmeBezeichnung(), //

        isStimmeingabenGruppeStimmenDirektstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenDirektstimmeVergleichBezeichnung(), //

        isStimmeingabenGruppeStimmenListenstimmeRendered(), //
        getStimmeingabenGruppeStimmenListenstimmeBezeichnung(), //

        isStimmeingabenGruppeStimmenListenstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenListenstimmeVergleichBezeichnung() //
    );
  }

  @Override
  public List<StimmeingabeGruppeStimmen> getStimmeingabenGruppeStimmen() {
    return getStimmeingabeGruppeStimmenErfassenModelBuilder().layoutStimmeingabenGruppeStimmen(getStimmeingabeGruppeStimmenErfassenModel(), createStimmeingabeGruppeStimmenErfassenModelContext());
  }

  @Override
  public StimmeingabeGruppeStimmenTableLayout getStimmeingabenSystemGruppeStimmenBottomTableLayout() {
    return new StimmeingabeGruppeStimmenTableLayout( //
        isErfassungGesperrt(), //

        getStimmeingabeErfassenUmgangKennungDefault(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeKennungEinzahlMessage(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeKennungEinzahlMessage(), //

        true, //
        stimmeingabeKopfzahlMeta.getStimmeingabeBezeichnungEinzahlMessage(), //

        isStimmeingabenGruppeStimmenDirektstimmeRendered(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeAnzahlEingabeEinzahlMessage(), //

        isStimmeingabenGruppeStimmenDirektstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenDirektstimmeVergleichBezeichnung(), //

        isStimmeingabenGruppeStimmenListenstimmeRendered(), //
        stimmeingabeKopfzahlMeta.getStimmeingabeAnzahlEingabeEinzahlMessage(), //

        isStimmeingabenGruppeStimmenListenstimmeVergleichRendered(), //
        getStimmeingabenGruppeStimmenListenstimmeVergleichBezeichnung() //
    );
  }

  @Override
  public List<StimmeingabeGruppeStimmen> getStimmeingabenSystemGruppeStimmenBottom() {
    return getStimmeingabeGruppeStimmenErfassenModelBuilder().layoutStimmeingabenSystemGruppeStimmenBottom(getStimmeingabeGruppeStimmenErfassenModel(),
        createStimmeingabeGruppeStimmenErfassenModelContext());
  }

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

    stimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue.setGruppenImGebiet(getStimmeingabeGruppeStimmenReferencingListenkandidaturStimmenUnterseite().stream()
        .filter(StimmeingabeGruppeStimmen::hasGruppeImGebiet).map(StimmeingabeGruppeStimmen::getGruppeImGebiet).collect(toList()));
  }

  protected List<StimmeingabeGruppeStimmen> getStimmeingabeGruppeStimmenReferencingListenkandidaturStimmenUnterseite() {
    return getStimmeingabeGruppeStimmenErfassenModel().getStimmenPolitischeGruppen().stream()
        .filter(stimmeingabeGruppeStimmen -> stimmeingabeGruppeStimmen != null && getStimmeingabeReferencingListenkandidaturStimmenUnterseite(stimmeingabeGruppeStimmen) != null
            && !stimmeingabeGruppeStimmen.hasSystemGruppenStimmeingabe() && stimmeingabeGruppeStimmen.hasStimmeingabeDieKeineZwischensummeIst())
        .collect(toList());
  }

  @Nullable
  protected Stimmeingabe getStimmeingabeReferencingListenkandidaturStimmenUnterseite(@Nonnull StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen) {
    return stimmeingabeGruppeStimmen.getStimmeingabeListenstimme();
  }

  public boolean isAnyListenkandidaturStimmeingabeFuerGruppeImGebietVorhanden(GruppeImGebiet gruppeImGebiet) {
    return getStimmeingabenLetzterErgebniseingang().stream().anyMatch(stimmeingabe -> stimmeingabe.hasListenkandidatur() && stimmeingabe.getGruppeImGebiet().equals(gruppeImGebiet));
  }

  @Override
  protected Collection<Stimmeingabe> getStimmeingabenFuerDiesenView() {
    Collection<Stimmeingabe> result = new HashSet<>(super.getStimmeingabenFuerDiesenView());

    for (StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen : getStimmeingabenSystemGruppeStimmen()) {
      result.addAll(stimmeingabeGruppeStimmen.getStimmeingaben());
    }

    for (StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen : getStimmeingabenGruppeStimmen()) {
      result.addAll(stimmeingabeGruppeStimmen.getStimmeingaben());
    }

    for (StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen : getStimmeingabenSystemGruppeStimmenBottom()) {
      result.addAll(stimmeingabeGruppeStimmen.getStimmeingaben());
    }

    return result;
  }

  @Override
  protected StimmeingabeErfassenViewStatus createStimmeingabeErfassenViewStatus() {
    if (isAktuellerErgebniseingangErgebniseingabestufeLaesstEingabeZu() && hasPermission(UseCasePermission.STIMMEINGABE_FEHLER_BESTAETIGEN)) {
      if (isLetzterErgebniseingangAktuellVorhandenHavingStatusUngueltigKannFehler()) {
        addMessageAktuellerErgebniseingangErfassungGesperrtByStatusUngueltigKannFehlerKorrigierbar();

        return StimmeingabeErfassenViewStatus.FEHLER_BESTAETIGEN;
      }
    }

    if (isLetzterErgebniseingangAktuellVorhandenHavingStatusGueltig() || isLetzterErgebniseingangAktuellVorhandenHavingStatusUngueltigKannFehler()) {
      return StimmeingabeErfassenViewStatus.NUR_LESEN;
    }

    return super.createStimmeingabeErfassenViewStatus();
  }

  @Override
  protected String getStimmeingabeBezug(Stimmeingabe stimmeingabe) {
    if (isKopfzahlStimmeingabe(stimmeingabe)) {
      return super.getStimmeingabeBezug(stimmeingabe);
    }

    return getStimmeingabeGruppeStimmenErfassenLabelGenerator().getGruppeStimmenStimmeingabeKennung(getEntityAufStimmzettelDaten(), stimmeingabe, getGruppenImGebiet());
  }

  @Override
  @Redirect
  public String reset() {
    stimmeingabeListenkandidaturStimmenErfassenViewGruppeImGebietConversationQueue.reset();

    return super.reset();
  }

  protected List<StimmeingabePlausiFehler> collectStimmeingabePlausiFehlerReferencingListenkandidaturStimmenUnterseite(Stimmeingabefehlerart fehlerart) {
    List<StimmeingabePlausiFehler> result = new ArrayList<>();

    Map<GruppeImGebiet, List<Stimmeingabefehler>> stimmeingabefehlerListenkandidaturStimmenUnterseitePerGruppeImGebiet = collectStimmeingabefehlerLetzterErgebniseingangReferencingListenkandidaturStimmenUnterseite()
        .stream().filter(stimmeingabefehler -> stimmeingabefehler.getGruppeImGebiet() != null && stimmeingabefehler.getFehlerart().equals(fehlerart))
        .collect(groupingBy(Stimmeingabefehler::getGruppeImGebiet));

    for (StimmeingabeGruppeStimmen stimmeingabeGruppeStimmen : getStimmeingabeGruppeStimmenReferencingListenkandidaturStimmenUnterseite()) {
      GruppeImGebiet gruppeImGebiet = stimmeingabeGruppeStimmen.getGruppeImGebiet();

      List<Stimmeingabefehler> stimmeingabefehlerListenkandidaturStimmenUnterseite = stimmeingabefehlerListenkandidaturStimmenUnterseitePerGruppeImGebiet.getOrDefault(gruppeImGebiet, emptyList());

      if (!stimmeingabefehlerListenkandidaturStimmenUnterseite.isEmpty()) {
        Stimmeingabefehler stimmeingabefehlerBeliebig = stimmeingabefehlerListenkandidaturStimmenUnterseite.get(0);

        Stimmeingabefehler stimmeingabefehler = new Stimmeingabefehler();
        stimmeingabefehler.setFehlerart(fehlerart);
        stimmeingabefehler.setBeschreibung(getMessage(MESSAGE_KEY_FEHLER_AUF_UNTERSEITE));

        String stimmeingabeBezug = "";
        String stimmeingabeBezugId = "";

        Stimmeingabe stimmeingabe = getStimmeingabeReferencingListenkandidaturStimmenUnterseite(stimmeingabeGruppeStimmen);

        if (stimmeingabe != null) {
          stimmeingabeBezug = stimmeingabeGruppeStimmen.getStimmeingabeListenstimmeKennung();
          stimmeingabeBezugId = getStimmeingabeBezugId(stimmeingabe);

          String stimmeingabeStyleClass = "";

          if (Stimmeingabefehlerart.ERROR.equals(fehlerart)) {
            stimmeingabeStyleClass = StimmeingabeStimmen.STYLE_CLASS_ERROR;
          } else if (Stimmeingabefehlerart.WARNING.equals(fehlerart)) {
            stimmeingabeStyleClass = StimmeingabeStimmen.STYLE_CLASS_WARNING;
          }

          stimmeingabeGruppeStimmen.addStimmeingabeStyleClass(stimmeingabe, stimmeingabeStyleClass);
        }

        if (isBlank(stimmeingabeBezug)) {
          stimmeingabeBezug = getStimmeingabeGruppeStimmenErfassenLabelGenerator().getGruppeStimmenKennung(getEntityAufStimmzettelDaten(), gruppeImGebiet,
              stimmeingabefehlerBeliebig.getErgebniseingangsart(), getGruppenImGebiet());
        }

        result.add(new StimmeingabePlausiFehler(getMessages(), stimmeingabefehler, stimmeingabeBezugId, stimmeingabeBezug));
      }
    }

    return result;
  }

  private Collection<Stimmeingabefehler> collectStimmeingabefehlerLetzterErgebniseingangReferencingListenkandidaturStimmenUnterseite() {
    Collection<Stimmeingabefehler> result = new ArrayList<>();

    Set<EntityTK> stimmeingabeTks = getStimmeingabeTksFuerDiesenView();

    for (Stimmeingabefehler stimmeingabefehler : getStimmeingabefehlerLetzterErgebniseingang()) {
      Stimmeingabe stimmeingabe = stimmeingabefehler.getStimmeingabe();

      if (!stimmeingabeTks.contains(stimmeingabe.getTk())) {
        result.add(stimmeingabefehler);
      }
    }

    return result;
  }
}
