"""
Classes and functions for interacting with system management daemons.

arkOS Core
(c) 2016 CitizenWeb
Written by Jacob Cook
Licensed under GPLv3, see LICENSE.md
"""

import ldap
import ldap.modlist
import xmlrpc.client


from .utilities import errors
from dbus import SystemBus, Interface


class ConnectionsManager:
    """Manages arkOS connections to system-level processes via their APIs."""

    def __init__(self, config, secrets):
        self.config = config
        self.secrets = secrets

    def connect(self):
        """Initialize the connections."""
        self.connect_services()
        self.connect_ldap()

    def connect_services(self):
        self.DBus = SystemBus()
        self.SystemD = self.SystemDConnect(
            "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager")
        self.Supervisor = supervisor_connect()

    def connect_ldap(self):
        self.LDAP = ldap_connect(
            config=self.config, passwd=self.secrets.get("ldap")
        )

    def SystemDConnect(self, path, interface):
        systemd = self.DBus.get_object("org.freedesktop.systemd1", path)
        return Interface(systemd, dbus_interface=interface)


def ldap_connect(
        uri="", rootdn="", dn="cn=admin", config=None, passwd="",
        conn_type=""):
    """
    Initialize a connection to arkOS LDAP.

    :param str uri: LDAP host URI
    :param str rootdn: Root DN
    :param str dn: User DN
    :param Config config: arkOS config to use for default values
    :param str passwd: Password to use to validate credentials
    :returns: LDAP connection object
    """
    if not all([uri, rootdn, dn]) and not config:
        raise errors.InvalidConfigError("No LDAP values passed")
    uri = uri or config.get("general", "ldap_uri")
    rootdn = rootdn or config.get("general", "ldap_rootdn")
    conn_type = conn_type or config.get("general", "ldap_conntype")

    if conn_type == "dynamic":
        c = ldap.ldapobject.ReconnectLDAPObject(
            uri, retry_max=3, retry_delay=5.0)
    else:
        c = ldap.initialize(uri)

    try:
        c.simple_bind_s("{0},{1}".format(dn, rootdn), passwd)
    except ldap.INVALID_CREDENTIALS:
        raise errors.ConnectionError("LDAP", "Invalid username/password")
    except Exception as e:
        raise errors.ConnectionError("LDAP") from e
    if dn != "cn=admin":
        data = c.search_s("cn=admins,ou=groups,{0}".format(rootdn),
                          ldap.SCOPE_SUBTREE, "(objectClass=*)",
                          ["member"])[0][1]["member"]
        if "{0},{1}".format(dn, rootdn) not in data:
            raise errors.ConnectionError("LDAP", "Not an administrator")
    return c


def supervisor_connect():
    """
    Initialize a connection to Supervisor via XML-RPC API.

    :returns: XML-RPC connection object
    """
    try:
        s = xmlrpc.client.Server("http://localhost:9001/RPC2")
        return s.supervisor
    except Exception as e:
        raise errors.ConnectionError("Supervisor") from e