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

import java.util.Date;
import java.util.Map;
import java.util.function.Function;

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

public interface CustomContextParameter {
  Logger LOG = LoggerFactory.getLogger(CustomContextParameter.class);

  String NOW = "NOW";

  Map<String, Object> getContextParameters();

  default boolean containsKey(String key) {
    if (key == null) {
      return false;
    }
    return getContextParameters().containsKey(key);
  }

  default <C> C get(String key, Class<C> valueClazz) {
    if (key == null) {
      return null;
    }

    var result = getContextParameters().get(key);

    if (result != null && valueClazz != null && valueClazz.isInstance(result)) { // ELECT-9126, doppelter Check erforderlich!!!
      return (C) result;
    }

    return null;
  }

  default <C> C getOrDefault(String key, Class<C> valueClazz, C defaultValue) {
    if (key == null) {
      return defaultValue;
    }

    var result = getContextParameters().getOrDefault(key, defaultValue);

    if (result != null && valueClazz != null && isValueClazzInstance(valueClazz, result)) { // ELECT-9126, doppelter Check erforderlich!!!
      return (C) result;
    }

    return defaultValue;
  }

  static <C> boolean isValueClazzInstance(Class<C> valueClazz, Object result) {
    if (valueClazz.isInstance(result)) {
      return true;
    }
    LOG.warn("No instance: " + valueClazz + ", " + result.getClass());
    return false;
  }

  default <C> C computeIfAbsent(String key, Class<C> valueClazz, Function<String, C> factory) {
    if (containsKey(key)) {
      return get(key, valueClazz);
    }

    var result = factory.apply(key);
    put(key, result);
    return result;
  }

  default void put(String key, Object value) {
    if (key == null) {
      return;
    }
    if (value == null) {
      getContextParameters().remove(key);
    } else {
      getContextParameters().put(key, value);
    }
  }

  default void putAll(Map<String, Object> another) {
    for (var entry : another.entrySet()) {
      if (entry.getValue() == null) {
        getContextParameters().remove(entry.getKey());
      } else {
        getContextParameters().put(entry.getKey(), entry.getValue());
      }
    }
  }

  default Date getNow() {
    var now = get(NOW, Date.class);
    if (now == null) {
      now = new Date();
      setNow(now);
    }

    return now;
  }

  default void setNow(Date now) {
    put(NOW, now);
  }

}
