"""
automua™ is a trademark of "Gaspard d'Hautefeuille" and may not be used 
by third parties without the prior written permission of the author.

Copyright © 2019-2022 Ralph Seichter

This file is part of automua.

automua is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

automua is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with automua. If not, see <https://www.gnu.org/licenses/>.
"""
from collections import namedtuple

from ldap3 import Connection
from ldap3 import Server

from automua import log

STATUS_SUCCESS = 0
STATUS_ERROR = 1
STATUS_NO_MATCH = 2

LookupResult = namedtuple('LookupResult', 'status cn uid')


class LdapAccess:
    def __init__(self, hostname, port=636, use_ssl=True, user=None, password=None) -> None:
        self._server = Server(hostname, port=port, use_ssl=use_ssl)
        self._connection = Connection(self._server, lazy=False, read_only=True, user=user, password=password)

    def lookup(self, search_base: str, search_filter: str, attr_uid='uid', attr_cn=None) -> LookupResult:
        if not self._connection.bind():  # pragma: no cover (bind errors are not expected during unittests)
            log.error(f'LDAP bind failed: {self._connection.result}')
            return LookupResult(STATUS_ERROR, None, None)
        attributes = [attr_uid]
        if attr_cn:
            attributes.append(attr_cn)
        self._connection.search(search_base, search_filter, attributes=attributes, size_limit=1)
        if self._connection.response:
            ldap_entry = self._connection.response[0]
            log.debug(f'LDAP match {ldap_entry["dn"]}')
            cn = self.get_attribute(ldap_entry, attr_cn)
            uid = self.get_attribute(ldap_entry, attr_uid)
            result = LookupResult(STATUS_SUCCESS, cn, uid)
        else:
            log.warning(f'No LDAP match for filter {search_filter}')
            result = LookupResult(STATUS_NO_MATCH, None, None)
        self._connection.unbind()
        log.debug(result)
        return result

    @staticmethod
    def get_attribute(ldap_entry: dict, attribute: str):
        attributes = ldap_entry['attributes']
        if attribute and attribute in attributes:
            value = attributes[attribute]
            if isinstance(value, str):  # pragma: no cover (Will not happen when testing against OpenLDAP)
                log.debug(f'Returning string "{value}"')
                return value
            elif isinstance(value, list):
                log.debug(f'Returning list element "{value[0]}"')
                return value[0]
            log.error(f'Unexpected lookup result type: {type(value).__name__}')
        elif attribute:
            log.warning(f"Attribute '{attribute}' not found")
        return None
