#-*-coding:utf-8*-*
from pwd import getpwnam
from time import sleep
from os import listdir, system
from os.path import isdir, join
from pytest import raises

from scribe.eoleuser import User, get_quota, set_quota
from scribe.eleves import Eleve
from scribe.enseignants import Enseignant
from scribe.responsables import Responsable
from scribe.eolegroup import Group
from scribe.ldapconf import OPT_PATH, GROUP_PATH, LDAP_MODE
from scribe.errors import MissingUserCreateKey, NotEmptyGroup


if LDAP_MODE == 'ad':
    attr='cn'
else:
    attr='uid'


## Variables utilisés pour les tests
niveau_test = 'niveau_test'
option = 'xxx_opt_xxx'

classe_test = {'name':'classe_test',
               'niveau':niveau_test}

classe_test2 = {'name':'classe_test2',
               'niveau':niveau_test}

eleve_test = {'login':'__eleve__test__',
              'password':'Eleveeole$',
              'nom':'__test__',
              'prenom':'__eleve__',
              'date':'12/12/1983',
              'classe':classe_test['name'],
              'niveau':niveau_test,
              'civilite':'0',
              'domaine':'internet',
              'numero':'1',
              'quota':'20',
              'profil':'2',
              'shell':False,
              'patronyme':'nom_patronymique',
              }
dico_ent_prof = {}
prof_test = {'login':'__prof__test__',
             'password':'Profeole$',
             'nom':'__prof__',
             'prenom':'__test__',
             'date':'12/03/1954',
             'classe':'',
             'civilite':'1',
             'mail':'internet',
             'quota':'0',
             'profil':'1',
             'shell':True,
             'dico_ent':dico_ent_prof,
            }

dico_ent_parent = {}
resp_test = {'login':'__parent__test__',
             'password':'parenteole',
               'nom':'__test__',
               'prenom':'__eleve__',
               'mail':'parent@ifrance.fr',
               'eleve':eleve_test['login'],
               'date':'01/01/1111'
              }
resp_test2 = {'login':'__parent__test__2',
             'password':'parenteole',
               'nom':'__test__',
               'prenom':'__eleve__',
               'mail':'internet',
               'eleve':eleve_test['login'],
              }

workstation_test = '__machine_de_test__'

class TestUser:
    """
        Test la classe de base User
    """
    username = '__test_eoleuser_user__'

    def setup_class(cls):
        cls.user = User()
        cls.user.ldap_admin.connect()

    def test_add(self):
        self.user._add(login=self.username, password='eole')

    def test_filterargs(self):
        raises(MissingUserCreateKey, self.user._add, login='__user__error__login__')

    # FIXME : test invalide car la meta classe User crée des utilisateurs "virtuels"
    #def test_auth(self):
    #    self.user.auth(self.username, 'eole')

    def test_delete(self):
        self.user._delete(self.username)

    def teardown_class(cls):
        cls.user.ldap_admin.close()

class TestEleve:
    """
        Test de la classe Eleve
    """
    username = eleve_test['login']
    homedir = join('/home', 'adhomes', username)
    userftpdir = join(homedir, '.ftp')
    usergroupesdir = join(homedir, 'groupes')

    def setup_class(cls):
        cls.eleve = Eleve()
        cls.eleve.ldap_admin.connect()
        cls.group = Group()
        cls.group.ldap_admin.connect()

    def test_add(self):
        self.group._add('Niveau', niveau_test, sync=False)
        self.group._add('Classe', classe_test['name'], niveau=niveau_test, sync=False)
        self.group._add('Classe', classe_test2['name'], niveau=niveau_test, sync=False)
        self.eleve._add(**eleve_test)

    def test_is_user(self):
        assert self.eleve._is_user(self.username)
        assert self.eleve._get_attr(self.username, 'Meflcf')
        # attributs étendus
        assert self.eleve._get_attr(self.username, 'ENTPersonNomPatro') == [eleve_test['patronyme']]
        assert self.eleve._get_attr(self.username, 'ENTPersonProfils') == ['eleve']

    def test_auth(self):
        assert self.eleve.auth(self.username, eleve_test['password'])
        assert not self.eleve.auth(self.username, 'badpassword')

    def test_quota(self):
        assert get_quota(self.username) == 20
        set_quota(self.username, '0')
        assert get_quota(self.username) == 0
        set_quota(self.username, '20')

    def test_mail(self):
        maildir = self.eleve._get_attr(self.username, 'mailDir')[0]
        assert maildir != ''
        idx = 0
        while True:
            sleep(1)
            try:
                assert set(listdir(maildir)) == set(['cur', 'new', 'tmp'])
            except FileNotFoundError as err:
                idx += 1
                if idx == 10:
                    raise err from err
            else:
                break

    def test_set_attr(self):
        self.eleve._set_attr(self.username, 'Ine', '666')
        assert self.eleve._get_attr(self.username, 'Ine')[0] == '666'

    def test_change_classe(self):
        # liens symboliques avant
        assert isdir(join(self.userftpdir, classe_test['name']))
        assert not isdir(join(self.userftpdir, classe_test2['name']))
        assert isdir(join(self.usergroupesdir, classe_test['name']))
        assert not isdir(join(self.usergroupesdir, classe_test2['name']))
        self.eleve._change_classe(self.username, classe_test2['name'])
        assert self.eleve._get_attr(self.username, 'Divcod')[0] == classe_test2['name']
        sleep(0.5)
        if LDAP_MODE == 'ad':
            for member in self.group._get_attr(classe_test2['name'], 'member'):
                if member.startswith('CN={},'.format(self.username)):
                    break

            else:
                raise Exception('impossible de trouver {} dans le groupe {}'.format(self.username, classe_test2['name']))
            for member in self.group._get_attr(classe_test['name'], 'member'):
                if member.startswith('CN={},'.format(self.username)):
                    raise Exception('ne devrait pas trouver {} dans le groupe {}'.format(self.username, classe_test['name']))
        else:
            assert self.username in self.group._get_attr(classe_test2['name'], 'memberUid')
            assert self.username not in self.group._get_attr(classe_test['name'], 'memberUid')
        # liens symboliques après
        assert not isdir(join(self.userftpdir, classe_test['name']))
        assert isdir(join(self.userftpdir, classe_test2['name']))
        assert not isdir(join(self.usergroupesdir, classe_test['name']))
        assert isdir(join(self.usergroupesdir, classe_test2['name']))

    def test_add_option(self):
        self.group._add('Option', option, sync=False)
        assert isdir(join(OPT_PATH, option))
        self.eleve._inscription(self.username, option)
        assert isdir(join(OPT_PATH, option, self.username))
        assert isdir(join(self.userftpdir, option))
        assert isdir(join(self.usergroupesdir, option))

    # FIXME : possibilité de supprimer une option non vide ?
    #def test_delete_not_empty_option(self):
    #    raises(NotEmptyGroup, self.group._delete, option, sync=False)

    def test_delete_not_empty_classe(self):
        raises(NotEmptyGroup, self.group._delete, classe_test2['name'], sync=False)

    def test_delete_option(self):
        self.eleve._desinscription(self.username, option)
        assert not isdir(join(OPT_PATH, option, self.username))
        assert not isdir(join(self.userftpdir, option))
        assert not isdir(join(self.usergroupesdir, option))
        # le partage est toujours là
        assert isdir(join(GROUP_PATH, option))
        self.group._delete(option, rmdir=True, sync=False)
        assert not isdir(join(OPT_PATH, option))

    def test_delete(self):
        system('touch /home/tuser.txt')
        system('chown {0} /home/tuser.txt'.format(self.username))
        uid = str(getpwnam(self.username).pw_uid)
        assert get_quota(uid) == 20
        self.eleve._delete(self.username, remove_data=True)
        assert not self.eleve._is_user(self.username)
        assert get_quota(uid) == 0
        self.group._delete(classe_test['name'], sync=False)
        self.group._delete(classe_test2['name'], sync=False)
        self.group._delete(niveau_test, sync=False)
        system('rm -f /home/tuser.txt')

    def teardown_class(cls):
        cls.eleve.ldap_admin.close()
        cls.group.ldap_admin.close()


class TestProf:
    """
        Test de la classe Enseignant
    """
    username = prof_test['login']

    def setup_class(cls):
        cls.prof = Enseignant()
        cls.prof.ldap_admin.connect()

    def test_add(self):
        self.prof._add(**prof_test)

    def test_is_user(self):
        assert self.prof._is_user(self.username)
        assert self.prof._get_attr(self.username, 'typeadmin')
        assert self.prof._get_attr(self.username, 'ENTPersonProfils') == ['enseignant']

    def test_quota(self):
        assert get_quota(self.username) == 0
        set_quota(self.username, '50')
        assert get_quota(self.username) == 50

    def test_mail(self):
        maildir = self.prof._get_attr(self.username, 'mailDir')[0]
        assert maildir != ''
        idx = 0
        while True:
            sleep(1)
            try:
                assert set(listdir(maildir)) == set(['cur', 'new', 'tmp'])
            except FileNotFoundError as err:
                idx += 1
                if idx == 10:
                    raise err from err
            else:
                break

    def test_delete(self):
        self.prof._delete(self.username, remove_data=True)
        assert not self.prof._is_user(self.username)

    def teardown_class(cls):
        cls.prof.ldap_admin.close()

class TestResponsable:
    """
        test de la classe Responsable
    """
    username = resp_test['login']
    username2 = resp_test2['login']

    def setup_class(cls):
        cls.resp = Responsable()
        cls.resp.ldap_admin.connect()

    def test_add(self):
        self.resp._add(**resp_test)
        self.resp._add(**resp_test2)

    def test_is_user(self):
        assert self.resp._is_user(self.username)
        assert self.resp._is_user(self.username2)
        assert self.resp._get_attr(self.username, 'ENTPersonProfils') == ['responsable']

    def test_mon_eleve(self):
        assert self.resp._get_attr(self.username, 'ENTAuxPersRelEleveEleve')[0].startswith('%s=%s' % (attr, resp_test['eleve']))
        assert self.resp._get_attr(self.username, 'eleve')[0] == resp_test['eleve']

    def test_auth(self):
        if LDAP_MODE != 'ad':
            assert self.resp.auth(self.username, resp_test['password'])
            assert not self.resp.auth(self.username, 'badpass')

    def test_mail(self):
        maildir = self.resp._get_attr(self.username2, 'mailDir')[0]
        assert maildir != ''
        idx = 0
        while True:
            sleep(1)
            try:
                assert set(listdir(maildir)) == set(['cur', 'new', 'tmp'])
            except FileNotFoundError as err:
                idx += 1
                if idx == 10:
                    raise err from err
            else:
                break

    def test_delete(self):
        self.resp._delete(self.username, remove_data=True)
        self.resp._delete(self.username2, remove_data=True)
        sleep(0.5)
        assert not self.resp._is_user(self.username)
        assert not self.resp._is_user(self.username2)

    def teardown_class(cls):
        cls.resp.ldap_admin.close()


