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

import de.ivu.elect.business.konfiguration.control.CustomParameterTemplateCache;
import de.ivu.elect.business.security.Customer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.faces.validator.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;

public interface CustomParameterTemplate {

  Logger LOG = LoggerFactory.getLogger(CustomParameterTemplate.class);

  String name();

  String getKey();

  List<Customer> getCustomers();

  CustomParameterKategorie getKategorie();

  KonfigurationNotify getKonfigurationNotify();

  default int getKategoriePosition() {
    return Integer.MAX_VALUE; // ELECT-10924
  }

  CustomParameterWertTyp getWertTyp();

  List<Validator> getWertValidators();

  Supplier<List<String>> getWertStringValues();

  Supplier<Function<String, String>> getWertStringValueToLabelFct();

  @Nullable
  Class< ? extends CustomParameterFilter< ? extends CustomParameter< ? extends CustomParameterTemplate>, ? extends Enum< ? >>> getFilter();

  Class< ? extends Enum< ? >> getWertEnumClass();

  default boolean isCustomerExcluded(Customer customer) {
    return false;
  }

  default boolean hasCustomer(Customer another) {
    return getCustomers().stream().anyMatch(customer -> customer.equals(another) && !isCustomerExcluded(customer));
  }

  default boolean hasCustomerOrParent(Customer another) {
    if (another == null) {
      return false;
    }

    var resultMap = CustomParameterTemplateCache.hasCustomerOrParentCache.computeIfAbsent(this, x -> new ConcurrentHashMap<>());
    var result = resultMap.get(another);

    if (result == null) {
      result = hasCustomerOrParentInternal(another);
      resultMap.put(another, result);
    }
    return result;
  }


  @Deprecated // ELECT-16595 nicht extern verwenden
  default boolean hasCustomerOrParentInternal(Customer another) {
    if (isCustomerExcluded(another)) {
      return false;
    }
    for (Customer customer : getCustomers()) {
      if (customer.equals(another) && !isCustomerExcluded(customer)) {
        return true;
      }

      for (Customer parent : another.getParents()) {
        if (hasCustomerOrParent(parent)) {
          return true;
        }
      }
    }

    return false;
  }

  static List<Enum<?>> allEnumValues(Class<? extends Enum<?>> enumClass) {
    List<Enum<?>> result = new ArrayList<>();

    if (enumClass != null) {
      try {
        for (Object enumValue : (Enum[]) enumClass.getMethod("values").invoke(enumClass)) {
          result.add((Enum<?>) enumValue);
        }
      } catch (Exception ex) {
        LOG.warn("Keine Enum Werte gefunden [enumClass = " + enumClass + "]", ex);
      }
    }

    return result;
  }
}
