
package de.ivu.elect.business.gebietsbaum.entity;

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

import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;

import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.wahl.entity.NlWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.util.CaseAndDiacriticIgnoringNormalizer;

public class NlGebiet {

  public static final String NBSB_REGION_NAME = "NBSB";

  public static final int NBSB_REGION_NUMMER = 9010; // ELECTVAPP-2030

  private static final Boolean WAHL_UNTERSTUETZER_ERFASSBAR_VALUE_BRIEFWAHL_GEMEINDE_UNZULAESSIG = Boolean.TRUE;

  // Bei Anpassungen auch NlWasGebietViewFormBuilder#addGebietsartProperty anpassen...
  private static final Boolean GEBIET_SONDERSTIMMBEZIRK_VALUE_BRIEFWAHLGEBIET_AUSLAND = Boolean.TRUE;

  private final Gebiet gebiet;

  public NlGebiet(Gebiet gebiet) {
    super();

    this.gebiet = gebiet;
  }

  private Wahl getWahl() {
    if (gebiet != null) {
      return gebiet.getWahl();
    }

    return null;
  }

  public NlGebietKorrekturenStimmeingabenStatus getKorrekturenStimmeingabenStatus() {
    if (gebiet != null) {
      return gebiet.getCustomData(NlGebietCustomData.class).getKorrekturenStimmeingabenStatus();
    }

    return NlGebietKorrekturenStimmeingabenStatus.UNBEKANNT;
  }

  public void setKorrekturenStimmeingabenStatus(NlGebietKorrekturenStimmeingabenStatus korrekturenStimmeingabenStatus) {
    if (gebiet != null) {
      gebiet.getCustomData(NlGebietCustomData.class).setKorrekturenStimmeingabenStatus(korrekturenStimmeingabenStatus);
    }
  }

  public boolean isBriefwahlGemeindeInGebietshierarchieUnterhalbZulaessig() {
    Wahl wahl = getWahl();

    return wahl != null && !WAHL_UNTERSTUETZER_ERFASSBAR_VALUE_BRIEFWAHL_GEMEINDE_UNZULAESSIG.equals(wahl.getUnterstuetzerErfassbar());
  }

  public void setBriefwahlGemeindeInGebietshierarchieUnterhalbZulaessig() {
    Wahl wahl = getWahl();

    if (wahl != null) {
      wahl.setUnterstuetzerErfassbar(!WAHL_UNTERSTUETZER_ERFASSBAR_VALUE_BRIEFWAHL_GEMEINDE_UNZULAESSIG);
    }
  }

  public void setBriefwahlGemeindeInGebietshierarchieUnterhalbUnzulaessig() {
    Wahl wahl = getWahl();

    if (wahl != null) {
      wahl.setUnterstuetzerErfassbar(WAHL_UNTERSTUETZER_ERFASSBAR_VALUE_BRIEFWAHL_GEMEINDE_UNZULAESSIG);
    }
  }

  public boolean isUrnenwahlgebietGemeinde() {
    return isUrnenwahlgebietGemeinde(gebiet);
  }

  public boolean isBriefwahlgebietGemeinde() {
    return isBriefwahlgebietGemeinde(gebiet);
  }

  public void setBriefwahlgebietGemeinde() {
    gebiet.setSonderstimmbezirk(!GEBIET_SONDERSTIMMBEZIRK_VALUE_BRIEFWAHLGEBIET_AUSLAND);
  }

  public boolean isBriefwahlAuslandInGebietshierarchieUnterhalbZulaessig() {
    return gebiet != null
        && (isRegionNameNbsb(gebiet.getName()) || /* ELECT-16710 */ NlWahlart.equalsWahlebene(getWahl(), NlWahlart.KUERZEL_WAHLEBENE_CSB) && isRegionNameSGravenhage(gebiet.getName()));
  }

  public boolean isBriefwahlgebietAusland() {
    return isBriefwahlgebietAusland(gebiet);
  }

  public void setBriefwahlgebietAusland() {
    gebiet.setSonderstimmbezirk(GEBIET_SONDERSTIMMBEZIRK_VALUE_BRIEFWAHLGEBIET_AUSLAND);
  }

  /**
   * NBSB (Nationaal BriefStemBureau) ist nun eine eigene WAS-Installation und (neben der Gemeinde 's-Gravenhage) eine eigene "Gemeinde" unterhalb des Kieskring 's-Gravenhage!!!
   *
   * @see NlWahlart#isNBSB
   * @see NlWahlart#isNBSBC
   */
  public static boolean isRegionNameNbsb(String gebietName) {
    return containsIgnoreCase(gebietName, NBSB_REGION_NAME); // ELECT-16655, "'s-Gravenhage" || "Den Haag" -> "NBSB"!!!
  }

  /**
   * HSBB (HoofdStemBureau met nationaal Briefstembureau) ist eine eigene/zweite WAS-Installation des Kieskring 's-Gravenhage mit dessen Gemeinde 's-Gravenhage (Urnenwahl) UND dem NBSB (Briefwahl
   * Ausland).
   *
   * @see NlWahlart#isHSBB
   */
  public static boolean isRegionNameHsbb(String gebietName) {
    return isRegionNameSGravenhage(gebietName);
  }

  private static boolean isRegionNameSGravenhage(String gebietName) {
    return containsIgnoreCase(gebietName, "Gravenhage") || containsIgnoreCase(gebietName, "Den Haag");
  }

  public static boolean isUrnenwahlgebietGemeinde(Gebiet gebiet) {
    return isUrnenwahlgebiet(gebiet);
  }

  public static boolean isBriefwahlgebietGemeinde(Gebiet gebiet) {
    return isBriefwahlgebiet(gebiet) && !GEBIET_SONDERSTIMMBEZIRK_VALUE_BRIEFWAHLGEBIET_AUSLAND.equals(gebiet.getSonderstimmbezirk());
  }

  public static boolean isBriefwahlgebietAusland(Gebiet gebiet) {
    return isBriefwahlgebiet(gebiet) && !isBriefwahlgebietGemeinde(gebiet);
  }

  private static boolean isUrnenwahlgebiet(Gebiet gebiet) {
    return gebiet != null && gebiet.isUrnenwahlgebiet();
  }

  private static boolean isBriefwahlgebiet(Gebiet gebiet) {
    return gebiet != null && gebiet.isBriefwahlgebiet();
  }

  public static boolean isListenwahlgebietenUebergeordnetesGebiet(Gebiet gebiet) {
    if (gebiet == null) {
      return false;
    }

    if (gebiet.isListenwahlgebiet()) {
      return false;
    }

    if (gebiet.hasUebergeordnetesGebiet()) {
      return isListenwahlgebietenUebergeordnetesGebiet(gebiet.getUebergeordnetesGebiet());
    }

    return true;
  }

  public static List<Gebiet> collectVoteValueGebiete(Wahl wahl, AbstractGebietDAO gebietDAO) {
    return collectVoteValueGebiete(wahl, () -> gebietDAO.findByWahl(wahl));
  }

  public static List<Gebiet> collectVoteValueGebiete(Wahl wahl, Supplier<Collection< ? extends Gebiet>> gebieteFct) {
    if (wahl == null || !NlWahlart.EERSTE_KAMER.equalsWahlart(wahl)) { // ELECT-13527
      return emptyList();
    }

    return gebieteFct.get().stream().filter(NlGebietsart::isVoteValueGebiet).sorted(GebietByBreadthFirstComparator.INSTANCE).collect(toList());
  }

  public static String getGebietNameFuerWahlId(String name) {
    if (name == null || isBlank(name)) {
      return name;
    }

    // ELECT-13672
    return CaseAndDiacriticIgnoringNormalizer.DEFAULT_INSTANCE.normalizeDiacritics(name //
        .replace(" ", "") //
        .replace("-", "") //
        .replace("(", "") // ELECTVAPP-1766
        .replace(")", "") //
        .replace("'", "") //
        .replace(",", "") // ELECTVAPP-1766
    );
  }
}
