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

import java.util.Map;
import java.util.function.Supplier;

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

import org.apache.commons.lang3.StringUtils;

import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.security.control.ElectCommonPrincipal;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsfehlerstatus;
import de.ivu.elect.jee.faces.Messages;
import de.ivu.elect.presentation.ergebnis.menu.ErgebnisseMainMenuEntry;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.security.GebietSecurity;
import de.ivu.elect.presentation.stimmeingabe.menu.StimmenMainMenuEntry;

@Specializes
public class WasGebietsbaumDynamicIconResolver extends GebietsbaumDynamicIconResolver {

  private static final long serialVersionUID = 1L;

  public static final String COLOR_ERROR = " color-error";

  public static final String COLOR_WARN = " color-warn";

  public static final String COLOR_INFO = " color-info";

  public static final String COLOR_SUCCCES = " color-success";

  @Inject
  private transient StimmenMainMenuEntry stimmenMainMenuEntry;

  @Inject
  private transient ErgebnisseMainMenuEntry ergebnisseMainMenuEntry;

  @Inject
  protected transient GebietSecurity gebietSecurity;

  @Inject
  private transient WasGebietsbaumDynamicStore wasGebietsbaumDynamicStore;

  @Inject
  private transient ElectCommonPrincipal principal;

  @Inject
  private transient Messages messages;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  public WasGebietsbaumDynamicIconResolver() {
    super();
  }

  @Override
  public boolean isDynamicIcon(Supplier<Gebiet> gebiet) {
    return super.isDynamicIcon(gebiet) || isRechtVorhanden() && (isErfassungsViewAktiv() || isErfasstInGesetzt(
        gebiet)/*ELECT-15075*/) && isEinsehenRechtVorhanden(
        gebiet.get());
  }

  private boolean isErfasstInGesetzt(Supplier<Gebiet> gebiet) {
    var g = gebiet.get();
    return g != null && g.castTo(Wahldurchfuehrungsgebiet.class).getEntity().getErfasstIn() != null;

  }

  private boolean isRechtVorhanden() {
    return principal.hasUseCasePermission(UseCasePermission.STIMMEINGABE_FORTSCHRITT_GEBIETSBAUM_ANZEIGEN);
  }

  private boolean isEinsehenRechtVorhanden(Gebiet gebiet) {
    return gebietSecurity.getEinsehenGebietSecurity().isRechtVorhanden(gebiet);
  }

  private transient Boolean erfassungsViewAktiv;

  protected boolean isErfassungsViewAktiv() {
    if (erfassungsViewAktiv == null) {
      erfassungsViewAktiv = stimmenMainMenuEntry.isSubTopicActive() || ergebnisseMainMenuEntry.isSubTopicActive()/*ELECT-12916*/;
    }
    return erfassungsViewAktiv;
  }

  @Override
  public String getDynamischesIcon(String icon, Supplier<Gebiet> gebiet) {
    var wahldurchfuehrungsgebiet = gebiet.get().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
    var wasGebietsbaumErfassungStatus = wasGebietsbaumDynamicStore.getWasGebietsbaumErfassungStatus(wahldurchfuehrungsgebiet);
    if (wahldurchfuehrungsgebiet.isErfassungsgebietLautGebietsart()) {
      return getErfassungsgebietDynmaischesIcon(icon, wahldurchfuehrungsgebiet, wasGebietsbaumErfassungStatus);
    } else {
      return getGebietDynamischesIcon(icon, wasGebietsbaumErfassungStatus);
    }
  }

  protected String getGebietDynamischesIcon(String icon, WasGebietsbaumErfassungStatus wasGebietsbaumErfassungStatus) {
    var warnIcon = bestimmeWarnIcon(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
    if (StringUtils.isNotBlank(warnIcon)) {
      return warnIcon;
    }
    if (wasGebietsbaumErfassungStatus.getAnzahlErfassungsgebieteUnterhalbInsgesamt() > 0L) {
      var anzahlGueltig = bestimmeAnzahlGueltig(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
      if (anzahlGueltig >= wasGebietsbaumErfassungStatus.getAnzahlErfassungsgebieteUnterhalbInsgesamt()) {
        return icons.getIconCircleCheck() + COLOR_SUCCCES;
      }
      return icons.getIconStatus(getProzent(wasGebietsbaumErfassungStatus.getAnzahlErfassungsgebieteUnterhalbInsgesamt(), anzahlGueltig));
    }
    return icon;
  }

  protected String getErfassungsgebietDynmaischesIcon(String icon, Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet,
                                                      WasGebietsbaumErfassungStatus wasGebietsbaumErfassungStatus) {
    if (wasGebietsbaumErfassungStatus.getAusbleibend().isAusbleibend()) {
      if (wasGebietsbaumErfassungStatus.getAusbleibend().isAusbleibendAnderesGebiet()) {
        return "fa fa-reply-all" + COLOR_INFO;
      } else {
        return "fa fa-remove" + COLOR_INFO;
      }
    }

    if (wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl().isEmpty()) {
      if (isBriefwahlgebiet(wahldurchfuehrungsgebiet)) {
        return "fa fa-envelope-o dimmed-icon";
      }
      if (isUrnenwahlgebiet(wahldurchfuehrungsgebiet)) {
        return "fa fa-file-o dimmed-icon";
      }
      return icon + " dimmed-icon";
    }
    var warnIcon = bestimmeWarnIcon(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
    if (StringUtils.isNotBlank(warnIcon)) {
      return warnIcon;
    }
    if (isBriefwahlgebiet(wahldurchfuehrungsgebiet)) {
      return "fa fa-envelope" + COLOR_SUCCCES;
    }
    if (isUrnenwahlgebiet(wahldurchfuehrungsgebiet)) {
      return "fa fa-file" + COLOR_SUCCCES;
    }
    return icon + COLOR_SUCCCES;
  }

  protected boolean isBriefwahlgebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return wahldurchfuehrungsgebiet.isBriefwahlgebiet();
  }

  protected boolean isUrnenwahlgebiet(Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet) {
    return wahldurchfuehrungsgebiet.isUrnenwahlgebiet();
  }

  @Override
  public String getDynamischenTitle(String title, Supplier<Gebiet> gebiet) {
    var wahldurchfuehrungsgebiet = gebiet.get().castTo(Wahldurchfuehrungsgebiet.class).getEntity();
    var wasGebietsbaumErfassungStatus = wasGebietsbaumDynamicStore.getWasGebietsbaumErfassungStatus(wahldurchfuehrungsgebiet);

    if (wahldurchfuehrungsgebiet.isErfassungsgebietLautGebietsart()) {
      if (wasGebietsbaumErfassungStatus.getAusbleibend().isAusbleibend()) {
        if (wasGebietsbaumErfassungStatus.getAusbleibend().isAusbleibendAnderesGebiet()) {
          return additionalTitle(title, "was.gebietsbaum.dynamic.title.wird.in.gebiet.erfasst",
              gebietNummerFormatter.getDarstellung(wahldurchfuehrungsgebiet.getErfasstIn()));
        }
        return additionalTitle(title, "was.gebietsbaum.dynamic.title.wird.nicht.erfasst");
      }

      if (wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl().isEmpty()) {
        return additionalTitle(title, "was.gebietsbaum.dynamic.title.nicht.erfasst");
      }

      if (isErrorVorhanden(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl())) {
        return additionalTitle(title, "was.gebietsbaum.dynamic.title.muss.fehler.vorhanden");
      }

      if (isWarningVorhanden(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl())) {
        return additionalTitle(title, "was.gebietsbaum.dynamic.title.kann.fehler.vorhanden");
      }

      return additionalTitle(title, "was.gebietsbaum.dynamic.title.erfasst");

    } else {
      var anzahlInsgesamt = wasGebietsbaumErfassungStatus.getAnzahlErfassungsgebieteUnterhalbInsgesamt();
      var anzahlMussFehler = bestimmeAnzahlMussFehler(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
      var anzahlKannFehler = bestimmeAnzahlKannFehler(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
      var anzahlGueltigErfasst = bestimmeAnzahlGueltig(wasGebietsbaumErfassungStatus.getErgebniseingangsfehlerstatusAnzahl());
      var anzahlNichtErfasst = anzahlInsgesamt - (anzahlGueltigErfasst + anzahlMussFehler + anzahlKannFehler);

      return additionalTitle(title, "was.gebietsbaum.dynamic.title.statistik", anzahlInsgesamt, anzahlMussFehler, anzahlKannFehler, anzahlGueltigErfasst,
          anzahlNichtErfasst);
    }
  }

  private String additionalTitle(String title, String msgKey, Object... params) {

    var additional = messages.getMessageForMessageKey(msgKey, params);
    if (StringUtils.isBlank(additional)) {
      return title;
    }
    if (StringUtils.isBlank(title)) {
      return additional;
    }

    return title + "\n" + additional;

  }

  private long bestimmeAnzahlGueltig(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.entrySet().stream().filter(entry -> entry.getKey().isGueltig() && entry.getValue() != null)
        .mapToLong(Map.Entry::getValue).sum();
  }

  private long bestimmeAnzahlMussFehler(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.entrySet().stream().filter(entry -> entry.getKey().isUngueltigMussFehler() && entry.getValue() != null)
        .mapToLong(Map.Entry::getValue).sum();
  }

  private long bestimmeAnzahlKannFehler(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.entrySet().stream().filter(entry -> entry.getKey().isUngueltigKannFehler() && entry.getValue() != null)
        .mapToLong(Map.Entry::getValue).sum();
  }

  @Nullable
  private String bestimmeWarnIcon(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    if (isErrorVorhanden(ergebniseingangsfehlerstatusAnzahl)) {
      return icons.getIconProblem() + COLOR_ERROR;
    }
    if (isWarningVorhanden(ergebniseingangsfehlerstatusAnzahl)) {
      return "fa fa-exclamation-circle" + COLOR_WARN;
    }

    return null;
  }

  private boolean isErrorVorhanden(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.keySet().stream().filter(Ergebniseingangsfehlerstatus::isUngueltigMussFehler)
        .anyMatch(ergebniseingangsfehlerstatus -> isVorhanden(ergebniseingangsfehlerstatus, ergebniseingangsfehlerstatusAnzahl));
  }

  private boolean isWarningVorhanden(Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.keySet().stream().filter(Ergebniseingangsfehlerstatus::isUngueltigKannFehler)
        .anyMatch(ergebniseingangsfehlerstatus -> isVorhanden(ergebniseingangsfehlerstatus, ergebniseingangsfehlerstatusAnzahl));
  }

  private boolean isVorhanden(Ergebniseingangsfehlerstatus ergebniseingangsfehlerstatus, Map<Ergebniseingangsfehlerstatus, Long> ergebniseingangsfehlerstatusAnzahl) {
    return ergebniseingangsfehlerstatusAnzahl.get(ergebniseingangsfehlerstatus) != null && ergebniseingangsfehlerstatusAnzahl.get(
        ergebniseingangsfehlerstatus) > 0L;
  }

}
