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

import java.io.IOException;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.ReflectionException;

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

import static java.lang.Boolean.TRUE;

/**
 *  on 24.05.2017.
 */
public class WildFlyServer implements Serializable {

  private static final Logger LOG = LoggerFactory.getLogger(WildFlyServer.class);
  private static final String IS_THORNTAIL_PROPERTY_KEY = "application.is-thorntail";

  private static final long serialVersionUID = 1L;

  public static final String HTTP_LISTENER = "jboss.as:subsystem=undertow,server=default-server,http-listener=default";

  public static final String HTTPS_LISTENER = "jboss.as:subsystem=undertow,server=default-server,https-listener=https";

  public static final String AJP_LISTENER = "jboss.as:subsystem=undertow,server=default-server,ajp-listener=default-ajp";

  @Inject
  transient DatasourceJndiResolver datasourceJndiResolver;

  public static boolean isThorntail() {
    // HINT, any thorntail application should set this property in its project-defaults.yml
    return TRUE.toString().equalsIgnoreCase(System.getProperty(IS_THORNTAIL_PROPERTY_KEY));
  }

  public int getDefaultTransactionTimeout() {
    // listWildFlyMBeans();
    Object value = getAttribut("jboss.as:subsystem=transactions", "defaultTimeout");
    if (value == null) {
      return 0;
    }
    return (Integer) value;
  }

  public long getHttpMaxPostSize() {
    Object value = getAttribut(HTTP_LISTENER, "maxPostSize");
    if (value == null) {
      return 0;
    }
    return (long) value;
  }

  public int getHttpMaxParameters() {
    Object value = getAttribut(HTTP_LISTENER, "maxParameters");
    if (value == null) {
      return 0;
    }
    return (int) value;
  }

  public long getHttpsMaxPostSize() {
    Object value = getAttribut(HTTPS_LISTENER, "maxPostSize");
    if (value == null) {
      return 0;
    }
    return (long) value;
  }

  public int getHttpsMaxParameters() {
    Object value = getAttribut(HTTPS_LISTENER, "maxParameters");
    if (value == null) {
      return 0;
    }
    return (int) value;
  }

  public boolean isHttpsHttp2Enabled() {
    Object value = getAttribut(HTTPS_LISTENER, "enable-http2");
    if (value == null) {
      return false;
    }
    return Boolean.TRUE.equals(value);
  }

  public boolean isHttpsHttp2PushEnabled() {
    Object value = getAttribut(HTTPS_LISTENER, "http2-enable-push");
    if (value == null) {
      return false;
    }
    return Boolean.TRUE.equals(value);
  }


  public long getAjpHttpMaxPostSize() {
    Object value = getAttribut(AJP_LISTENER, "maxPostSize");
    if (value == null) {
      return 0;
    }
    return (long) value;
  }

  public int getAjpHttpMaxParameters() {
    Object value = getAttribut(AJP_LISTENER, "maxParameters");
    if (value == null) {
      return 0;
    }
    return (int) value;
  }

  @Nullable
  private Object getAttribut(String objectName, String attributName) {
    MBeanServerConnection mbeanServerConnection = ManagementFactory.getPlatformMBeanServer();
    try {
      return getAttribute(attributName, mbeanServerConnection, new ObjectName(objectName));
    } catch (Exception e) {
      // ELECT-7903
      LOG.warn("Keine HTTP-Connector defunden: " + objectName + ", " + attributName + ", " + e.getMessage());
      return null;
    }
  }

  public int getMaxPoolSize() {
    Object value = getDatasourceAttribute("maxPoolSize");
    if (value == null) {
      return 0;
    }
    return (int) value;
  }

  @Nullable
  protected Object getDatasourceAttribute(String attributName) {
    String dsJndiName = datasourceJndiResolver.getJndiName();
    try {
      MBeanServerConnection mbeanServerConnection = ManagementFactory.getPlatformMBeanServer();

      ObjectName serviceRef = new ObjectName("jboss.as:subsystem=datasources,data-source=*");
      Set<ObjectName> mbeans = mbeanServerConnection.queryNames(serviceRef, null);
      for (ObjectName on : mbeans) {
        if (dsJndiName.equals(gatDatasourceJndiName(mbeanServerConnection, on))) {
          return getAttribute(attributName, mbeanServerConnection, on);
        }
      }
      // ELECT-7903
      LOG.warn("Keine Datenbank gefunden: " + dsJndiName);
    } catch (Exception e) {
      // ELECT-7903
      LOG.warn("Keine Datenbank gefunden: " + dsJndiName + ", " + e.getMessage());
    }
    // ELECT-7903
    return null;
  }

  @Nullable
  private Object gatDatasourceJndiName(MBeanServerConnection mbeanServerConnection, ObjectName on) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
    return getAttribute("jndiName", mbeanServerConnection, on);
  }

  @Nullable
  private Object getAttribute(String attributName, MBeanServerConnection mbeanServerConnection, ObjectName on) throws ReflectionException, MBeanException, InstanceNotFoundException, IOException {
    try {
      return mbeanServerConnection.getAttribute(on, attributName);
    } catch (AttributeNotFoundException e) {
      LOG.warn("Attribut nicht gefunden: '" + attributName + "', " + on);
      // WASDE-1062 Attribut nicht vorhanden? => null
      return null;
    }
  }

  private static void listWildFlyMBeans() {
    try {
      MBeanServerConnection connection = ManagementFactory.getPlatformMBeanServer();

      ObjectName serviceRef = new ObjectName("*.*:*");
      Set<ObjectName> mbeans = connection.queryNames(serviceRef, null);
      for (ObjectName on : mbeans) {
        LOG.info("\t ObjectName : " + on);
        try {
          printAttributes(connection, on);
        } catch (Exception exc) {

        }

      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  static void printAttributes(MBeanServerConnection connection, final ObjectName http)
      throws Exception {
    MBeanInfo info = connection.getMBeanInfo(http);
    MBeanAttributeInfo[] attrInfo = info.getAttributes();
    LOG.info(">Attributes:");
    for (MBeanAttributeInfo attr : attrInfo) {
      LOG.info("  " + attr.getName() + " (" + attr.getType() + ")");
    }
  }

}
