package de.ivu.elect.business.sitzberechnung.control.wahlbeteiligung.intern;

import de.ivu.elect.business.sitzberechnung.control.wahlbeteiligung.WahlberechnungContextDaten;
import de.ivu.elect.business.sitzberechnung.control.wahlbeteiligung.Wahlbeteiligungsgebiet;

public class WahlbeteiligungsgebietQueries {

  private final WahlberechnungContextDaten contextDaten;

  public WahlbeteiligungsgebietQueries(WahlberechnungContextDaten contextDaten) {
    this.contextDaten = contextDaten;
  }

  public long getGesamtanzahlWahlberechtigteRekursiv(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      return contextDaten.getAnzahlWahlberechtigteInsgesamt(gebiet);
    }
    long sum = 0;
    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      sum += getGesamtanzahlWahlberechtigteRekursiv(untergebiet);
    }
    return sum;
  }

  public long getGesamtanzahlAusgezaehlteWahlberechtigteInsgesamtRekursiv(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      if (contextDaten.isKopfzahlwerteVollstaendigErfasst(gebiet)) {
        return contextDaten.getAnzahlWahlberechtigteInsgesamt(gebiet);
      }
      return 0;
    }
    long sum = 0;
    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      sum += getGesamtanzahlAusgezaehlteWahlberechtigteInsgesamtRekursiv(untergebiet);
    }
    return sum;
  }

  private long getGesamtanzahlWaehlerMitWahlscheinRekursiv(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      return contextDaten.getAnzahlWaehlerMitWahlschein(gebiet);
    }
    long sum = 0;
    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      sum += getGesamtanzahlWaehlerMitWahlscheinRekursiv(untergebiet);
    }
    return sum;
  }

  private long getGesamtanzahlWahlberechtigteMitWahlscheinRekursiv(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      return contextDaten.getAnzahlWahlberechtigteMitWahlschein(gebiet);
    }
    long sum = 0;
    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      sum += getGesamtanzahlWahlberechtigteMitWahlscheinRekursiv(untergebiet);
    }
    return sum;
  }

  public Double getTatsaechlicheRuecklaufquote(Wahlbeteiligungsgebiet gebiet) {
    if (gebiet.isErfassungsgebiet()) {gebiet = contextDaten.getUebergeordnetesGebiet(gebiet);}

    while (gebiet != null && isMussEbeneHoeherGehenZurBerechnungTatsaechlicherRuecklaufquote(gebiet)) {
      gebiet = contextDaten.getUebergeordnetesGebiet(gebiet);
    }
    if (gebiet == null || isNichtAusgezaehltesBriefwahlgebietPresesnt_RekursivCheck(gebiet) ||
        isUrnenwahlgebietOhneErfassteWahlberechtigteMitWahlscheinPresent_RekursivCheck(gebiet)) {
      return null;
    }

    long gesamtanzahlWahlberechtigteMitWahlscheinRekursiv = getGesamtanzahlWahlberechtigteMitWahlscheinRekursiv(gebiet);
    if (gesamtanzahlWahlberechtigteMitWahlscheinRekursiv == 0) {
      return null;
    }
    long gesamtanzahlWaehlerMitWahlscheinRekursiv = getGesamtanzahlWaehlerMitWahlscheinRekursiv(gebiet);
    return 1.0 * gesamtanzahlWaehlerMitWahlscheinRekursiv / gesamtanzahlWahlberechtigteMitWahlscheinRekursiv;
  }

  private boolean isMussEbeneHoeherGehenZurBerechnungTatsaechlicherRuecklaufquote(Wahlbeteiligungsgebiet gebiet) {
    if (isWahlgebiet(gebiet)) {
      return false;
    }
    if(isBriefwahlgebietInUntergebietenNotPresentRekursiv(gebiet)) {
      return true;
    }
    if(!isWahlberechtigteMitWahlscheinVorhanden(gebiet)) {
      return true;
    }
    if (isHatEinUebergeordnetesGebietBriefwahlgebieteAlsDirekteUntergebiete(gebiet)) {
      return true;
    }
    return false;
  }

  private boolean isWahlgebiet(Wahlbeteiligungsgebiet gebiet) {
    return gebiet.isWahlgebiet();
  }

  private boolean isHatEinUebergeordnetesGebietBriefwahlgebieteAlsDirekteUntergebiete(Wahlbeteiligungsgebiet gebiet) {
    gebiet = contextDaten.getUebergeordnetesGebiet(gebiet);
    while (gebiet != null) {
      if (isBriefwahlgebietInDirektenUntergebietenPresent(gebiet)) {
        return true;
      }
      gebiet = contextDaten.getUebergeordnetesGebiet(gebiet);
    }
    return false;
  }

  public boolean isBriefwahlgebietInDirektenUntergebietenPresent(Wahlbeteiligungsgebiet obergebiet) {
    return contextDaten.getUntergeordneteGebiete(obergebiet).stream().anyMatch(Wahlbeteiligungsgebiet::isBriefwahlgebiet);
  }

  public boolean isBriefwahlgebietInUntergebietenNotPresentRekursiv(Wahlbeteiligungsgebiet gebiet) {
    if (gebiet.isBriefwahlgebiet()) {
      return false;
    }
    return contextDaten.getUntergeordneteGebiete(gebiet).stream().allMatch(kind -> isBriefwahlgebietInUntergebietenNotPresentRekursiv(kind));
  }

  private boolean isWahlberechtigteMitWahlscheinVorhanden(Wahlbeteiligungsgebiet gebiet) {
    return getGesamtanzahlWahlberechtigteMitWahlscheinRekursiv(gebiet) > 0;
  }

  private boolean isUrnenwahlgebietOhneErfassteWahlberechtigteMitWahlscheinPresent_RekursivCheck(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      return gebiet.isUrnenwahlgebiet() && isGebietOhneErfassteWahlberechtigte(gebiet);
    }

    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      if (isUrnenwahlgebietOhneErfassteWahlberechtigteMitWahlscheinPresent_RekursivCheck(untergebiet)) {
        return true;
      }
    }
    return false;
  }

  private boolean isGebietOhneErfassteWahlberechtigte(Wahlbeteiligungsgebiet gebiet) {
    return contextDaten.getAnzahlWahlberechtigteInsgesamt(gebiet) == 0;
  }

  private boolean isNichtAusgezaehltesBriefwahlgebietPresesnt_RekursivCheck(Wahlbeteiligungsgebiet gebiet) {
    if (contextDaten.isErfassungsgebiet(gebiet)) {
      return gebiet.isBriefwahlgebiet() && !contextDaten.isKopfzahlwerteVollstaendigErfasst(gebiet);
    }

    for (Wahlbeteiligungsgebiet untergebiet : contextDaten.getUntergeordneteGebiete(gebiet)) {
      if (isNichtAusgezaehltesBriefwahlgebietPresesnt_RekursivCheck(untergebiet)) {
        return true;
      }
    }
    return false;
  }
}
