import logging
import pytest
import os
from lib389 import Entry
from lib389._constants import *
from lib389.topologies import topology_st as topo
from lib389.utils import *
import ldap

DEBUGGING = os.getenv("DEBUGGING", default=False)
if DEBUGGING:
    logging.getLogger(__name__).setLevel(logging.DEBUG)
else:
    logging.getLogger(__name__).setLevel(logging.INFO)
log = logging.getLogger(__name__)

def config_memberof(server):

    server.plugins.enable(name=PLUGIN_MEMBER_OF)
    peoplebase = 'ou=people,%s' % SUFFIX
    MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
    server.modify_s(MEMBEROF_PLUGIN_DN, [(ldap.MOD_REPLACE, 'memberOfAllBackends', b'on'),
                                         (ldap.MOD_REPLACE, 'memberOfEntryScope', peoplebase.encode())])
def _disable_auto_oc_memberof(server):
    MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
    server.modify_s(MEMBEROF_PLUGIN_DN,
        [(ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsContainer')])


def _enable_auto_oc_memberof(server):
    MEMBEROF_PLUGIN_DN = ('cn=' + PLUGIN_MEMBER_OF + ',cn=plugins,cn=config')
    server.modify_s(MEMBEROF_PLUGIN_DN,
        [(ldap.MOD_REPLACE, 'memberOfAutoAddOC', b'nsMemberOf')])

def add_user(server, cn, testbase, desc, sleep=True):
    dn = 'cn=%s,%s' % (cn, testbase)
    log.fatal('Adding user (%s): ' % dn)
    server.add_s(Entry((dn, {'objectclass': ['top', 'person'],
                             'sn': 'user_%s' % cn,
                             'description': desc})))
    if sleep:
        time.sleep(2)
        
def test_ticketticket_49985(topo):
    """Specify a test case purpose or name here

    :id: 0
    :setup: Standalone Instance
    :steps:
        1. Fill in test case steps here
        2. And indent them like this (RST format requirement)
    :expectedresults:
        1. Fill in the result that is expected
        2. For each test step
    """
    config_memberof(topo.standalone)
    topo.standalone.restart(timeout=10)
    
    peoplebase = 'ou=people,%s' % SUFFIX
    for i in range(10):
        cn = 'user%d' % i
        add_user(topo.standalone, cn, peoplebase, 'add on standalone', sleep=False)
    #cn_out_scope = 'user_out0'
    #dn_out_scope = 'cn=%s,%s' % (cn_out_scope, SUFFIX)
    #add_user(topo.standalone, cn_out_scope, SUFFIX, 'add on standalone', sleep=False)
    
    group0_dn = 'cn=group_in0,%s' % peoplebase
    topo.standalone.add_s(Entry((group0_dn, {'objectclass': ['top', 'groupofnames'],
                             'member': [
                                   'cn=user0,%s' % peoplebase,
                                   'cn=user1,%s' % peoplebase,
                                   ],
                             'description': 'mygroup'})))
    
    # Check the those entries have memberof with group0
    for i in range(2):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        assert ent.hasAttr('memberof')
        found = False
        for val in ent.getValues('memberof'):
            topo.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group0_dn.encode().lower()))
            if val.lower() == group0_dn.encode().lower():
                found = True
                break
        assert found
    
    # Create a group1 out of the scope
    group1_dn = 'cn=group_out1,%s' % SUFFIX
    topo.standalone.add_s(Entry((group1_dn, {'objectclass': ['top', 'groupofnames'],
                             'member': [
                                   'cn=user0,%s' % peoplebase,
                                   'cn=user1,%s' % peoplebase,
                                   ],
                             'description': 'mygroup'})))
    # Check the those entries have not memberof with group1 
    for i in range(2):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        assert ent.hasAttr('memberof')
        found = False
        for val in ent.getValues('memberof'):
            topo.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, group1_dn.encode().lower()))
            if val.lower() == group1_dn.encode().lower():
                found = True
                break
        assert not found
    
    # move group1 into the scope and check user0 and user1 are memberof group1
    topo.standalone.rename_s(group1_dn, 'cn=group_in1', newsuperior=peoplebase, delold=0)
    new_group1_dn = 'cn=group_in1,%s' % peoplebase
    for i in range(2):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        assert ent.hasAttr('memberof')
        found = False
        for val in ent.getValues('memberof'):
            topo.standalone.log.info("!!!!!!! %s: memberof->%s (vs %s)" % (user_dn, val, new_group1_dn.encode().lower()))
            if val.lower() == new_group1_dn.encode().lower():
                found = True
                break
        assert found
        
    # Create a group2 out of the scope
    group2_dn = 'cn=group_out2,%s' % SUFFIX
    topo.standalone.add_s(Entry((group2_dn, {'objectclass': ['top', 'groupofnames'],
                             'member': [
                                   'cn=user2,%s' % peoplebase,
                                   'cn=user3,%s' % peoplebase,
                                   ],
                             'description': 'mygroup'})))
    # Check the those entries have not memberof with group1 
    for i in (2, 3):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        assert not ent.hasAttr('memberof')
        
    # memberof will not add the missing objectclass
    _disable_auto_oc_memberof(topo.standalone)
    topo.standalone.restart(timeout=10)
    
    # move group1 into the scope and check user0 and user1 are memberof group1
    try:
        topo.standalone.rename_s(group2_dn, 'cn=group_in2', newsuperior=peoplebase, delold=0)
        topo.standalone.log.info("Should assert on rename")
        assert False
    except ldap.CONSTRAINT_VIOLATION:
        pass
    
    # Check the those entries have memberof
    for i in (2, 3):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        topo.standalone.log.info("Should assert %s has memberof is %s" % (user_dn, ent.hasAttr('memberof')))
        assert ent.hasAttr('memberof')
        
    # Create a group3 in the scope
    group3_dn = 'cn=group3_in,%s' % SUFFIX
    try:
        topo.standalone.add_s(Entry((group3_dn, {'objectclass': ['top', 'groupofnames'],
                             'member': [
                                   'cn=user4,%s' % peoplebase,
                                   'cn=user5,%s' % peoplebase,
                                   ],
                             'description': 'mygroup'})))
        topo.standalone.log.info("Should assert on add")
        assert False
    except ldap.CONSTRAINT_VIOLATION:
        pass
    # Check the those entries have memberof
    for i in (4, 5):
        user_dn = 'cn=user%d,%s' % (i, peoplebase)
        ent = topo.standalone.getEntry(user_dn, ldap.SCOPE_BASE, "(objectclass=*)", ['memberof'])
        topo.standalone.log.info("Should assert %s has memberof is %s" % (user_dn, ent.hasAttr('memberof')))
        assert ent.hasAttr('memberof')
        

    # If you need any test suite initialization,
    # please, write additional fixture for that (including finalizer).
    # Topology for suites are predefined in lib389/topologies.py.

    # If you need host, port or any other data about instance,
    # Please, use the instance object attributes for that (for example, topo.ms["master1"].serverid)

    if DEBUGGING:
        # Add debugging steps(if any)...
        pass


if __name__ == '__main__':
    # Run isolated
    # -s for DEBUG mode
    CURRENT_FILE = os.path.realpath(__file__)
    pytest.main(["-s", CURRENT_FILE])

