
package de.ivu.elect.presentation.gebietsbaum.form;

import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import javax.enterprise.inject.Specializes;
import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.control.AWerte;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.GebietProperties;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gebietsbaum.entity.WahldurchfuehrungsgebietConstants;
import de.ivu.elect.business.gebietsbaum.entity.WahldurchfuehrungsgebietProperties;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.stimmen.boundary.KopfzahlLabelGenerator;
import de.ivu.elect.business.wahl.boundary.WahlExtendedDAO;
import de.ivu.elect.business.wahl.boundary.WahlartExtendedDAO;
import de.ivu.elect.business.wahl.boundary.WasUmgangAWerteKonfiguration;
import de.ivu.elect.business.wahl.boundary.WasUmgangBWerteKonfiguration;
import de.ivu.elect.business.wahl.entity.AbstractWahlart;
import de.ivu.elect.business.wahl.entity.CommonWahlart;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.MessageProperty;
import de.ivu.elect.jee.presentation.dynamicform.PropertyType;
import de.ivu.elect.jee.presentation.dynamicform.ReadOnlyLongProperty;
import de.ivu.elect.presentation.LambdaEntitySelectOneMenuProperty;
import de.ivu.elect.presentation.gebietsbaum.AnzahlWahlberechtigteErfassungState;


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

@Specializes
public class WasGebietViewFormBuilder extends CoreGebietViewFormBuilder {

  private static final long serialVersionUID = 1L;

  private static final Pattern COMPILE = Pattern.compile("\\s+");

  @Inject
  protected transient AnzahlWahlberechtigteErfassungState anzahlWahlberechtigteErfassungState;

  @Inject
  private transient WasUmgangAWerteKonfiguration umgangAWerteKonfiguration;

  @Inject
  private transient WasUmgangBWerteKonfiguration umgangBWerteKonfiguration;

  @Inject
  protected transient KopfzahlLabelGenerator kopfzahlLabelGenerator;

  @Inject
  private transient WahlartExtendedDAO wahlartExtendedDAO;

  @Inject
  private transient WahlExtendedDAO wahlExtendedDAO;

  public WasGebietViewFormBuilder() {
    super();
  }

  @Override
  public void addGebietProperties(FormBuilder formBuilder, Gebiet gebiet, boolean readonly) {
    super.addGebietProperties(formBuilder, gebiet, readonly);

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = asWahldurchfuehrungsgebiet(gebiet);
    WahldurchfuehrungsgebietProperties wahldurchfuehrungsgebietProperties = wahldurchfuehrungsgebiet.getProperties();

    // ELECTDD-331 - B2 Werte müssen erfasst werden können
    boolean briefwahlInUrneErfassenPropertyRendered = isBriefwahlInUrneErfassbar(wahldurchfuehrungsgebiet);

    if (briefwahlInUrneErfassenPropertyRendered) {
      formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getBriefwahlInUrneErfassenProperty());
    }

    // ELECTKOM-213, addAnzahlWahlberechtigteProperties(formBuilder, wahldurchfuehrungsgebiet);

    if (gebiet.getWahl() != null && CommonWahlart.BUNDESTAGSWAHL.equalsWahlart(gebiet.getWahl()) && gebiet.isErfassungsgebiet()) {
      formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getErfassungsbesonderheitProperty()).withReadonly(readonly);
    }
  }

  private boolean isBriefwahlInUrneErfassbar(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return wahldurchfuehrungsgebiet.isErfassungsgebiet() && (umgangBWerteKonfiguration.isWaehlerMitWahlscheinInUrnenwahlAnzahlAusUrnenwahlBeiGebietsartRelevant(wahldurchfuehrungsgebiet)
        || umgangBWerteKonfiguration.isWaehlerMitWahlscheinInUrnenwahlAnzahlAusBriefwahlBeiGebietsartRelevant(wahldurchfuehrungsgebiet));
  }

  @Override
  protected boolean isExterneIdVisibleOnPageLoad(Gebiet gebiet) {
    return hasGebietsartErfassungsgebiet(gebiet) && umgangAWerteKonfiguration.isPruefkennzeichenWahlberechtigteImportNotwendig(getSelectedWahl());
  }

  @Override
  protected void addBemerkungProperties(FormBuilder formBuilder, Gebiet gebiet, boolean readonly) {
    super.addBemerkungProperties(formBuilder, gebiet, readonly);

    formBuilder.addProperty(asWahldurchfuehrungsgebiet(gebiet).getProperties().getTextFuerPraesentationProperty()).withType(PropertyType.INPUT_TEXTAREA);
  }

  protected void addAnzahlWahlberechtigteProperties(FormBuilder formBuilder, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, boolean readonly, Supplier<List<Wahldurchfuehrungsgebiet>> erfasstFuerGebiete) {
    addAnzahlWahlberechtigteProperties(formBuilder, wahldurchfuehrungsgebiet, readonly, "gebiet.edit.anzahl.wahlberechtigter.label", null, erfasstFuerGebiete);
  }

  protected void addAnzahlWahlberechtigteProperties(FormBuilder formBuilder, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet, boolean readonly,
                                                    String separatorMsgKey, String separatorAdditionalParameter,
                                                    Supplier<List<Wahldurchfuehrungsgebiet>> erfasstFuerGebiete) {
    WahldurchfuehrungsgebietProperties wahldurchfuehrungsgebietProperties = wahldurchfuehrungsgebiet.getProperties();

    boolean anzahlWahlberechtigteOhneWahlscheinRendered = isAnzahlWahlberechtigteOhneWahlscheinRendered(wahldurchfuehrungsgebiet);
    boolean anzahlWahlberechtigteMitWahlscheinRendered = isAnzahlWahlberechtigteMitWahlscheinRendered(wahldurchfuehrungsgebiet);
    boolean anzahlWahlberechtigteNichtImWaehlerverzeichnisRendered = isAnzahlWahlberechtigteNichtImWaehlerverzeichnisRendered(wahldurchfuehrungsgebiet);

    boolean anzahlWahlberechtigteGesamtSummandenRendered = anzahlWahlberechtigteOhneWahlscheinRendered || anzahlWahlberechtigteMitWahlscheinRendered
        || anzahlWahlberechtigteNichtImWaehlerverzeichnisRendered;
    boolean anzahlWahlberechtigteGesamtSummandenReadonly = readonly || isAnzahlWahlberechtigteGesamtSummandenReadonly(wahldurchfuehrungsgebiet);

    boolean anzahlWahlberechtigeOhneWahlscheinReadOnly = anzahlWahlberechtigteErfassungState.isAnzahlWahlberechtigeOhneWahlscheinReadOnly(
        wahldurchfuehrungsgebiet);

    boolean anzahlWahlberechtigteGesamtRendered = isAnzahlWahlberechtigteGesamtRendered(wahldurchfuehrungsgebiet);

    if (anzahlWahlberechtigteGesamtSummandenRendered || anzahlWahlberechtigteGesamtRendered) {
      formBuilder.addSeparator(separatorMsgKey, kopfzahlLabelGenerator.getWahlberechtigteBezeichnungPlural(wahldurchfuehrungsgebiet),
          separatorAdditionalParameter);
    }

    if (!anzahlWahlberechtigteGesamtSummandenReadonly && anzahlWahlberechtigteErfassungState.isErfassungGesperrtByErgebniseingang(wahldurchfuehrungsgebiet)) {
      formBuilder.addProperty(new MessageProperty(getMessage("anzahl.wahlberechtigte.edit.warning.override")));
      formBuilder.withStyleClass("warningMessageProperty");
    }

    String labelOhneWahlschein = "";
    if (anzahlWahlberechtigteOhneWahlscheinRendered) {
      labelOhneWahlschein = kopfzahlLabelGenerator.getAnzahlWahlberechtigteOhneWahlscheinKennung(wahldurchfuehrungsgebiet);

      formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getAnzahlWahlberechtigteOhneWahlscheinProperty()) //
          .withLabel(labelOhneWahlschein) //
          .withReadonly(anzahlWahlberechtigteGesamtSummandenReadonly || anzahlWahlberechtigeOhneWahlscheinReadOnly) //
          .withVisibleOnPageLoad(true);
    }
    String labelMitWahlschein = "";
    if (anzahlWahlberechtigteMitWahlscheinRendered) {
      labelMitWahlschein = kopfzahlLabelGenerator.getAnzahlWahlberechtigteMitWahlscheinKennung(wahldurchfuehrungsgebiet);

      formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getAnzahlWahlberechtigteMitWahlscheinProperty()) //
          .withLabel(labelMitWahlschein) //
          .withReadonly(anzahlWahlberechtigteGesamtSummandenReadonly) //
          .withVisibleOnPageLoad(true);
    }

    String labelNichtImWaehlerverzeichnis = "";
    if (anzahlWahlberechtigteNichtImWaehlerverzeichnisRendered) {
      labelNichtImWaehlerverzeichnis = kopfzahlLabelGenerator.getAnzahlWahlberechtigteNichtImWaehlerverzeichnisKennung(wahldurchfuehrungsgebiet);

      formBuilder.addProperty(wahldurchfuehrungsgebietProperties.getAnzahlWahlberechtigteNichtImWaehlerverzeichnisProperty()) //
          .withLabel(labelNichtImWaehlerverzeichnis) //
          .withReadonly(anzahlWahlberechtigteGesamtSummandenReadonly) //
          .withVisibleOnPageLoad(true);
    }
    String labelGesamt = "";
    if (anzahlWahlberechtigteGesamtRendered) {
      labelGesamt = kopfzahlLabelGenerator.getAnzahlWahlberechtigteGesamtBezeichnung(wahldurchfuehrungsgebiet);

      if (anzahlWahlberechtigteGesamtSummandenRendered) {
        String anzahlWahlberechtigteGesamtSummanden = kopfzahlLabelGenerator
            .getAnzahlWahlberechtigteGesamtOhneWahlscheinPlusMitWahlscheinPlusNichtImWaehlerverzeichnisSummanden(wahldurchfuehrungsgebiet);

        if (!anzahlWahlberechtigteNichtImWaehlerverzeichnisRendered) {
          anzahlWahlberechtigteGesamtSummanden = kopfzahlLabelGenerator.getAnzahlWahlberechtigteGesamtOhneWahlscheinPlusMitWahlscheinSummanden(wahldurchfuehrungsgebiet);
        }

        labelGesamt += " (" + COMPILE.matcher(anzahlWahlberechtigteGesamtSummanden).replaceAll("\u00A0") + ")";
      }

      formBuilder.addProperty(new AnzahlWahlberechtigteGesamtProperty(wahldurchfuehrungsgebiet)) //
          .withLabel(labelGesamt) //
          .withReadonly(true) // NOTE, dass dieser Wert immer 'readonly' ist, da er immer BERECHNET wird!
          .withVisibleOnPageLoad(true);
    }

    List<Wahldurchfuehrungsgebiet> wahldurchfuehrungsgebiets = erfasstFuerGebiete.get();
    if (!wahldurchfuehrungsgebiets.isEmpty()) {
      AWerte aWerteGesamt = new AWerte();
      aWerteGesamt.add(wahldurchfuehrungsgebiet);
      for (Wahldurchfuehrungsgebiet erfasstFuer : wahldurchfuehrungsgebiets) {
        aWerteGesamt.add(erfasstFuer);
        addAnzahlWahlberechtigteProperties(formBuilder, erfasstFuer, true, "gebiet.edit.anzahl.wahlberechtigter.von.gebiet.label",
            gebietNummerFormatter.getDarstellung(erfasstFuer),
            Collections::emptyList);
      }
      formBuilder.addSeparator("gebiet.edit.anzahl.wahlberechtigter.gesamt.label");
      if (anzahlWahlberechtigteOhneWahlscheinRendered) {
        formBuilder.addProperty(new ReadOnlyLongProperty(labelOhneWahlschein, aWerteGesamt.getAnzahlWahlberechtigteOhneWahlschein()))
            .withVisibleOnPageLoad(true);
      }
      if (anzahlWahlberechtigteMitWahlscheinRendered) {
        formBuilder.addProperty(new ReadOnlyLongProperty(labelMitWahlschein, aWerteGesamt.getAnzahlWahlberechtigteMitWahlschein())).withVisibleOnPageLoad(true);
      }
      if (anzahlWahlberechtigteNichtImWaehlerverzeichnisRendered) {
        formBuilder.addProperty(new ReadOnlyLongProperty(labelNichtImWaehlerverzeichnis, aWerteGesamt.getAnzahlWahlberechtigteNichtImWaehlerverzeichnis()))
            .withVisibleOnPageLoad(true);
      }
      if (anzahlWahlberechtigteGesamtRendered) {
        formBuilder.addProperty(new ReadOnlyLongProperty(labelGesamt, aWerteGesamt.getAnzahlWahlberechtigteGesamtA1A2A3())).withVisibleOnPageLoad(true);
      }

    }

  }

  @Override
  protected void addVerantwortlicherMandant(FormBuilder formBuilder, Gebiet gebiet, boolean readonly) {
    super.addVerantwortlicherMandant(formBuilder, gebiet, readonly);

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = asWahldurchfuehrungsgebiet(gebiet);
    List<Wahlart> wahlarten = wahlartExtendedDAO.findByAllOrderByName(wahldurchfuehrungsgebiet.getMandant());

    LambdaEntitySelectOneMenuProperty<Wahlart> wahlartBeiAufteilungProperty = new LambdaEntitySelectOneMenuProperty<>(wahlarten, wahldurchfuehrungsgebiet::setWahlartBeiAufteilung,
        wahldurchfuehrungsgebiet::getWahlartBeiAufteilung, AbstractWahlart::getNameGuiEindeutig, WahldurchfuehrungsgebietConstants.MESSAGE_KEY_WAHLART_BEI_AUFTEILUNG_ID_EINZAHL, false, true);
    wahlartBeiAufteilungProperty.setNoSelectionLabel(getMessage("gebiet.wahlart.bei.aufteilung.leer.label"));
    formBuilder.addProperty(wahlartBeiAufteilungProperty).withReadonly(readonly); // ELECT-9522
  }

  private boolean isAnzahlWahlberechtigteOhneWahlscheinRendered(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return isAnzahlWahlberechtigteRendered(wahldurchfuehrungsgebiet) && umgangAWerteKonfiguration.isAnzahlWahlberechtigteOhneWahlscheinAmGebietErfassbar(wahldurchfuehrungsgebiet.getWahl());
  }

  private boolean isAnzahlWahlberechtigteMitWahlscheinRendered(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return isAnzahlWahlberechtigteRendered(wahldurchfuehrungsgebiet) && umgangAWerteKonfiguration.isAnzahlWahlberechtigteMitWahlscheinAmGebietErfassbar(wahldurchfuehrungsgebiet.getWahl());
  }

  private boolean isAnzahlWahlberechtigteNichtImWaehlerverzeichnisRendered(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return isAnzahlWahlberechtigteRendered(wahldurchfuehrungsgebiet) && umgangAWerteKonfiguration.isAnzahlWahlberechtigteNichtImWaehlerverzeichnisAmGebietErfassbar(wahldurchfuehrungsgebiet.getWahl());
  }

  private boolean isAnzahlWahlberechtigteGesamtRendered(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return isAnzahlWahlberechtigteRendered(wahldurchfuehrungsgebiet);
  }

  protected boolean isAnzahlWahlberechtigteGesamtSummandenReadonly(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return anzahlWahlberechtigteErfassungState.isErfassungGesperrtLautGebietsart(wahldurchfuehrungsgebiet) ||
        (anzahlWahlberechtigteErfassungState.isErfassungGesperrt(wahldurchfuehrungsgebiet) && !hasUseCasePermission(
            UseCasePermission.ANZAHL_WAHLBERECHTIGTE_MIT_ERGEBNISEINGANG_EDIT));
  }

  private boolean isAnzahlWahlberechtigteRendered(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return anzahlWahlberechtigteErfassungState.isErfassungMoeglich(wahldurchfuehrungsgebiet);
  }

  @Override
  protected void addGebietNummerProperty(Gebiet gebiet, FormBuilder formBuilder, boolean readonly, GebietProperties gebietProperties) {
    boolean nummerFixiert = readonly || isNotBlank(gebiet.getNummer()) && (isWahlBerichtetAn(gebiet) || isWahlIsAufgeteilt(gebiet)); // ELECT-8124

    super.addGebietNummerProperty(gebiet, formBuilder, nummerFixiert, gebietProperties);
  }

  private boolean isWahlIsAufgeteilt(Gebiet gebiet) {
    return !gebiet.getVerantwortlicheMandantenWahlen(wahlExtendedDAO).isEmpty();
  }

  private boolean isWahlBerichtetAn(Gebiet gebiet) {
    return gebiet.getWahl() != null && gebiet.getWahl().getBerichtetAn() != null;
  }

  protected Wahldurchfuehrungsgebiet asWahldurchfuehrungsgebiet(Gebiet gebiet) {
    return gebiet.castTo(Wahldurchfuehrungsgebiet.class).getEntity();
  }
}
