import logging
import pytest
import ldap
from lib389.idm.group import Groups
from lib389.idm.user import UserAccounts, TEST_USER_PROPERTIES
from lib389.plugins import MemberOfPlugin, ReferentialIntegrityPlugin
import os
from lib389._constants import *
from lib389.topologies import topology_st as topo
from lib389.backend import Backends
from lib389.index import Indexes
from lib389.utils import ldap, os, logging, ensure_bytes

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__)


TEST_ENTRY_NAME = 'testuser'
TEST_GROUP_NAME = 'group1'


def _member_index_tune(topo, value):
    backends = Backends(topo)
    backend = backends.get(DEFAULT_BENAME)
    indexes = backend.get_indexes()

    for i in indexes.list():
        i_cn = i.get_attr_val_utf8('cn')
        if i_cn.lower() == 'member':
            i.replace('nsIndexType', value)
            

def test_ticket49561(topo):
    """Specify a test case purpose or name here

    :id: c4df8d93-91de-4189-9681-7a918e69649c
    :setup: Standalone Instance
    :steps:
        1. Create a test_user
        2. Create a test_group and make test_user member of test_group
        3. Disable memberof plugin, so that when test_user is deleted, test_group is not updated
        4. Break the 'member' index, making it ignore 'eq'
        5. remove test_user. test group is not updated and member: =<test_user>.dn --> test_group.entryId remains
        6. reenable 'eq' for member index
        7. Enable Referential integrity
        8. Enable filter logging (error log)
        9. delete test_user -> referint will search 'member=<test_user>.dn' and will Bypass the filter
    :expectedresults:
    """
    
    inst = topo.standalone
    inst.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'65536'),
                              (ldap.MOD_REPLACE, 'nsslapd-plugin-logging', b'on'),
                              (ldap.MOD_REPLACE, 'nsslapd-accesslog-level', b'260')])

    
    memberof = MemberOfPlugin(inst)
    memberof.enable()
    inst.restart()
    
    
    # Create a user
    log.info('Adding user {}'.format(TEST_ENTRY_NAME))
    users = UserAccounts(topo.standalone, DEFAULT_SUFFIX, rdn=None)
    test_user = users.create(properties=TEST_USER_PROPERTIES)
    
    # Create a group and add the user into that group
    groups = Groups(inst, DEFAULT_SUFFIX, rdn=None)
    group_properties = {
        'cn': TEST_GROUP_NAME,
        'description': 'testgroup'}
    test_group = groups.create(properties=group_properties)
    test_group.add_member(test_user.dn)
    
    # Here member index refers test_user as member of test_group
    # disable memberof
    memberof.disable()
    inst.restart()

    
    # At this point 'member' has the equality key to test_user
    # prevent to delete it
    _member_index_tune(inst,['pres'])
    inst.restart()

    # Now remove the attribute from the group, the equality key
    # will not be removed
    mod = [(ldap.MOD_DELETE, 'member', ensure_bytes(test_user.dn))]
    inst.modify_s(test_group.dn, mod)

    # Now restore the equality for 'member' index
    # at the point the index says something that does not match what is in the DB
    _member_index_tune(inst,['eq','pres'])
    inst.restart()

    # enabling referential integrity
    referint = ReferentialIntegrityPlugin(inst)
    referint.enable()
    inst.restart()
    
    # now delete the user
    # member index will say that it is member of test_group
    inst.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', b'32')])
    test_user.delete()
    
    # Check we skip filter evaluation for
    # the Referential intergity plugin search on 'member=<test_user>.dn'
    # that was indexed, 
    pattern = ".*ldbm_back_next_search_entry_ext - Bypassing filter test.*"
    assert inst.ds_error_log.match(pattern)
    
    # 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])

