import re
import ldap
from webapp import config

def is_valid_name(name):
  """`true` if `name` is a safe ldap uid/cn"""
  return re.match(r'^[a-zA-Z_][a-zA-Z0-9-_\.]*\Z', name) is not None

def validate_name(name):
  """Raises `RuntimeError` if `name` is not a safe ldap uid/cn"""
  if not is_valid_name(name):
    raise RuntimeError('Invalid name')

def user_dn(uid):
  validate_name(uid)
  return config.ldap_user_dn_format.format(uid)

def group_dn(cn):
  validate_name(cn)
  return config.ldap_group_dn_format.format(cn)

def wrap(filter):
  if len(filter) and filter[0] == '(' and filter[-1] == ')':
    return filter
  else:
    return f'({filter})'

def _or(*filters):
  wrapped = ''.join(wrap(f) for f in filters)
  return f'(|{wrapped})'

def _and(*filters):
  wrapped = ''.join(wrap(f) for f in filters)
  return f'(&{wrapped})'

def _not(filter):
  wrapped = wrap(filter)
  return f'(!{wrapped})'

def member_of_any(groups):
  """Returns a filter that matches users that are a member of any of the given group names"""
  return _or(*(f'memberOf={group_dn(group)}' for group in groups))

def groups_of_user(uid):
  """Returns a filter that matches groups that have the given user as a member"""
  return f'(&(objectClass=groupOfUniqueNames)(uniqueMember={user_dn(uid)}))'

def normalized_entries(entries):
  """
  Converts ldap entries from python-ldap format into a more convenient
  List[Tuple[
    dn,
    List[tuple[attr_name, attr_value]]
  ]]
  """
  normalized = []
  for dn, attrs in entries:
    normalized_attrs = []
    for attr, values in attrs.items():
      for value in values:
        normalized_attrs.append((attr, value.decode()))
    normalized.append((dn, normalized_attrs))

  return normalized


