package de.ivu.elect.business.security.control;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.business.CurrentCustomer;
import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.control.EntityMitWurzelgebietDAO;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.business.security.UseCasePermission;
import de.ivu.elect.business.wahl.boundary.WahlSessionSelect;
import de.ivu.elect.business.wahl.entity.NlWahlCustomData;
import de.ivu.elect.business.wahl.entity.NlWahlPhase;
import de.ivu.elect.business.wahl.entity.NlWahlart;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlPK;
import de.ivu.elect.presentation.mandant.MandantSessionSelect;

@Specializes
public class NlElectExtendedUseCasePermissionFilter extends ElectExtendedUseCasePermissionFilter {

  private static final Logger LOG = LoggerFactory.getLogger(NlElectExtendedUseCasePermissionFilter.class);

  private static final long serialVersionUID = 1L;

  @Inject
  private transient WahlSessionSelect wahlSessionSelect;

  @Inject
  private transient CurrentCustomer currentCustomer;

  @Inject
  private transient MandantSessionSelect mandantSessionSelect;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  private static final Map<WahlPK, Set<UseCasePermission>> DENYLIST_CACHE = new ConcurrentHashMap<>();

  private static Set<UseCasePermission> DENYLIST_CACHE_KEINE_WAHL = null;

  @Inject
  private transient NlSessionIpBindRequestScoped nlSessionIpBindRequestScoped;

  @Override
  protected <T> boolean test(UseCasePermission useCasePermission, T object, Predicate<T> bestimme) {
    nlSessionIpBindRequestScoped.checkIpSession(); // ELECTVAPP-2074
    return super.test(useCasePermission, object, bestimme);
  }

  @Override
  protected Set<UseCasePermission> getAllowlist() {
    var wahlPk = wahlSessionSelect.getEntityPK();

    Set<UseCasePermission> denylist;
    if (wahlPk == null) {
      denylist = DENYLIST_CACHE_KEINE_WAHL;
    } else {
      denylist = DENYLIST_CACHE.get(wahlPk);
    }

    if (denylist == null) {
      var wahl = wahlSessionSelect.getEntity();
      denylist = bestimmeDenylist(wahl);
      if (wahl == null) {
        setDenylistKeineWahl(denylist);
      } else {
        DENYLIST_CACHE.put(wahl.getPk(), denylist);
      }
    }

    Set<UseCasePermission> result = new HashSet<>(super.getAllowlist());
//    System.out.println("result = " + result);
//    System.out.println("denylist = " + denylist);
    result.removeAll(denylist);
    return result;
  }

  private void setDenylistKeineWahl(Set<UseCasePermission> denylist) {
    synchronized (DENYLIST_CACHE) {
      DENYLIST_CACHE_KEINE_WAHL = denylist;
    }
  }

  private Set<UseCasePermission> bestimmeDenylist(@Nullable Wahl wahl) {

    Set<UseCasePermission> result = new HashSet<>();

    // Die Obermenge aller "NL-Allowlists" definiert die abzuziehenden Rechte
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_INITIAL_GEMEINDE_ALLOWLIST);

    result.addAll(NlUseCasePermissionAllowDenylist.WAS_INITIAL_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_ERFASSUNG_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_ABSCHLUSS_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_SITZBERECHNUNG_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WAS_FREIGABE_ALLOWLIST);

    result.addAll(NlUseCasePermissionAllowDenylist.WUS_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WUS_INITIAL_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WUS_EINREICHUNG_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WUS_PRUEFUNG_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WUS_NUMMERIERUNG_ALLOWLIST);
    result.addAll(NlUseCasePermissionAllowDenylist.WUS_FREIGABE_ALLOWLIST);

    // Je nach Wahlart - Rechte wieder freigeben
    var customer = currentCustomer.getCustomer();

    if (NlWahlart.isWas(customer)) {
      result.removeAll(NlUseCasePermissionAllowDenylist.WAS_ALLOWLIST);
      if (wahl == null) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_INITIAL_ALLOWLIST); // ohne Wahl soll initiale Phase aktiv sein
      } else {
        var nlWahlPhase = bestimmeWahlPhase(wahl);
        if (nlWahlPhase.isInitial() && NlWahlart.isWasGemeinde(() -> bestimmeWurzelgebiet(wahl), customer)) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_INITIAL_GEMEINDE_ALLOWLIST);
        }
        if (nlWahlPhase.isInitial()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_INITIAL_ALLOWLIST);
        }
        if (nlWahlPhase.isErfassung()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_ERFASSUNG_ALLOWLIST);
        }
        if (nlWahlPhase.isAbschluss()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_ABSCHLUSS_ALLOWLIST);
        }
        if (nlWahlPhase.isSitzberechnung()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_SITZBERECHNUNG_ALLOWLIST);
        }

        if (nlWahlPhase.isFreigabe()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WAS_FREIGABE_ALLOWLIST);
        }
      }
    }

    if (NlWahlart.isWus(customer)) {
      result.removeAll(NlUseCasePermissionAllowDenylist.WUS_ALLOWLIST);

      if (wahl == null) {
        result.removeAll(NlUseCasePermissionAllowDenylist.WUS_INITIAL_ALLOWLIST); // Ohne wahl soll initiale Phase aktiv sein
      } else {
        var nlWahlPhase = bestimmeWahlPhase(wahl);
        if (nlWahlPhase.isInitial()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WUS_INITIAL_ALLOWLIST);
        }
        if (nlWahlPhase.isEinreichung()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WUS_EINREICHUNG_ALLOWLIST);
        }
        if (nlWahlPhase.isPruefung()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WUS_PRUEFUNG_ALLOWLIST);
        }
        if (nlWahlPhase.isNummerierung()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WUS_NUMMERIERUNG_ALLOWLIST);
        }
        if (nlWahlPhase.isFreigabe()) {
          result.removeAll(NlUseCasePermissionAllowDenylist.WUS_FREIGABE_ALLOWLIST);
        }
      }
    }

    // ELECT-10289
//    // Denylist
//
//    if (NlWahlart.isWas(customer)) {
//
//      if (NlWahlart.isWasCSB(wahl, customer) || (wahl == null && nlSystemProperties.getDefaultElectionLevel(getMandant()).isCSB())) {
//        result.addAll(NlUseCasePermissionAllowDenylist.WAS_CSB_DENYLIST);
//      }
//
//      if (NlWahlart.isWasHSB(wahl, customer) || (wahl == null && nlSystemProperties.getDefaultElectionLevel(getMandant()).isHSB())) {
//        result.addAll(NlUseCasePermissionAllowDenylist.WAS_HSB_DENYLIST);
//      }
//
//      if (NlWahlart.isWasGSB(wahl, customer) || (wahl == null && nlSystemProperties.getDefaultElectionLevel(getMandant()).isGSB())) {
//        result.addAll(NlUseCasePermissionAllowDenylist.WAS_GSB_DENYLIST);
//      }
//
//    }

    return result;
  }

  private NlWahlPhase bestimmeWahlPhase(Wahl wahl) {
    try {
      return wahl.getCustomData(NlWahlCustomData.class).getNlWahlPhase();
    } catch (Exception e) {
      // Für Systeme/Mandanten ohne NlWahlCustomData
      return NlWahlPhase.NICHT_GESETZT;
    }
  }

  private Mandant getMandant() {
    return mandantSessionSelect.getMandant();
  }

  private Gebiet bestimmeWurzelgebiet(Wahl wahl) {
    return wahl.getWurzelgebiet(EntityMitWurzelgebietDAO.fromDao(gebietExtendedDAO));
  }

  public static void resetCache() {
    LOG.info("Reset Cache");
    DENYLIST_CACHE.clear();
  }
}
