import ldap
from passlib.hash import sha512_crypt
from email_validator import validate_email, EmailNotValidError
import subprocess

from .config import getDockersConfig, getSecretConfig

class Ldap:

    def __init__(self):
        self.ldap_connection = None
        self.ldap_root = getDockersConfig("ldap_root")
        self.ldap_admin_username = getSecretConfig("ldapServ", "LDAP_ADMIN_USERNAME")
        self.ldap_admin_password = getSecretConfig("ldapServ", "LDAP_ADMIN_PASSWORD")
        cmd="docker inspect -f '{{.NetworkSettings.Networks.ldapNet.IPAddress}}' ldapServ"
        self.ldap_host = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT).strip().decode()

    def __enter__(self):
        self.ldap_connection = ldap.initialize(f"ldap://{self.ldap_host}")
        self.ldap_connection.simple_bind_s("cn={},{}".format(self.ldap_admin_username, self.ldap_root), self.ldap_admin_password)
        return self

    def __exit__(self, tp, e, traceback):
        self.ldap_connection.unbind_s()


    def get_email(self, email):
        """
        Vérifier si un utilisateur avec cet email existe dans le LDAP soit comme mail principal soit comme alias
        """
        # Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
        filter_str = "(|(cn={})(mailAlias={}))".format(email, email)
        result = self.ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, filter_str)
        return result


    def get_users(self):
        """
        Renvoie tous les utilisateurs.
        """
        # Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
        result = self.ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_ONELEVEL)
        return result


    def get_mail_forwarding(self, email):
        """
        Renvoie le mail forwarding
        """
        # Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
        filter_str = "(|(cn={})(mailAlias={}))".format(email, email)
        result = self.ldap_connection.search_s("ou=mailForwardings,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, filter_str)
        return result


    def get_mail_forwardings(self):
        """
        Renvoie tous les mail forwardings.
        """
        # Créer une chaîne de filtre pour rechercher dans les champs "cn" et "mailAlias"
        result = self.ldap_connection.search_s("ou=mailForwardings,{}".format(self.ldap_root), ldap.SCOPE_ONELEVEL)
        return result


    def delete_user(self, email):
        """
        Supprimer un utilisateur du LDAP par son adresse e-mail
        """
        try:
            # Recherche de l'utilisateur
            result = self.ldap_connection.search_s("ou=users,{}".format(self.ldap_root), ldap.SCOPE_SUBTREE, "(cn={})".format(email))

            if not result:
                return False  # Utilisateur non trouvé

            # Récupération du DN de l'utilisateur
            dn = result[0][0]

            # Suppression de l'utilisateur
            self.ldap_connection.delete_s(dn)
            return True  # Utilisateur supprimé avec succès

        except ldap.NO_SUCH_OBJECT:
            return False  # Utilisateur non trouvé
        except ldap.LDAPError as e:
            return False  # Erreur lors de la suppression



    def create_user(self, email, prenom, nom, password, email_secours, quota):
        """
        Créer une nouvelle entrée dans le LDAP pour un nouvel utilisateur. QUESTION: A QUOI SERVENT PRENOM/NOM/IDENT_KAZ DANS LE LDAP ? POURQUOI 3 QUOTA ?
        """
        password_chiffre = sha512_crypt.hash(password)

        if not validate_email(email) or not validate_email(email_secours):
            return False

        if self.get_email(email):
            return False

        # Construire le DN
        dn = f"cn={email},ou=users,{self.ldap_root}"

        mod_attrs = [
          ('objectClass', [b'inetOrgPerson', b'PostfixBookMailAccount', b'nextcloudAccount', b'kaznaute']),
          ('sn', f'{prenom} {nom}'.encode('utf-8')),
          ('mail', email.encode('utf-8')),
          ('mailEnabled', b'TRUE'),
          ('mailGidNumber', b'5000'),
          ('mailHomeDirectory', f"/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
          ('mailQuota', f'{quota}G'.encode('utf-8')),
          ('mailStorageDirectory', f"maildir:/var/mail/{email.split('@')[1]}/{email.split('@')[0]}/".encode('utf-8')),
          ('mailUidNumber', b'5000'),
          ('mailDeSecours', email_secours.encode('utf-8')),
          ('identifiantKaz', f'{prenom.lower()}.{nom.lower()}'.encode('utf-8')),
          ('quota', str(quota).encode('utf-8')),
          ('nextcloudEnabled', b'TRUE'),
          ('nextcloudQuota', f'{quota} GB'.encode('utf-8')),
          ('mobilizonEnabled', b'TRUE'),
          ('agoraEnabled', b'TRUE'),
          ('userPassword', f'{{CRYPT}}{password_chiffre}'.encode('utf-8')),
          ('cn', email.encode('utf-8'))
        ]

        self.ldap_connection.add_s(dn, mod_attrs)
        return True


    def update_user(self, email, field, value):
        """
        Mettre à jour un champ.
        """
        if not validate_email(email):
            return False

        # Construire le DN
        dn = f"cn={email},ou=users,{self.ldap_root}"

        mod_attrs = [(ldap.MOD_REPLACE, field, value)]

        self.ldap_connection.modify_s(dn, mod_attrs)
        return True
