# -*- coding: utf-8 -*-
###########################################################################
# Eole NG - 2009
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
#
###########################################################################
"""
    Classe de gestion des users 'Eleves'
"""
from glob import glob
from os import unlink, makedirs
from os.path import join, isdir
from ldap import MOD_REPLACE, modlist
from fichier.quota import set_quota
from fichier.acl import set_owner, clear_acl, set_user_acl, copy_default_acl, \
    set_group_acl, set_default_acl
from scribe.ldapconf import SMB_SERVEUR, HOME_PATH, MAIL_DOMAIN, CLASS_PATH, \
    BRANCHE_ETAB2, ELEVE_FILTER, OPT_PATH, USER_FILTER, num_etab, AD_HOME_PATH, \
    REVERSE_LOGIN
from scribe.eoletools import add_dir, format_current_date, deformate_date, \
    add_symlink, format_old_date, replace_cars, fixdate, not_empty, \
    create_ad_home, get_reverse_login
from scribe.eoleuser import User, gen_common_attrs, send_first_mail, \
    gen_enteleve_attrs, gen_profil, gen_common_devdir, gen_radius_attrs
from scribe.importation.config import WRITE_PRIVE


class Eleve(User):
    """
        classe pour la gestion des élèves Scribe
    """
    _type = 'eleves'
    profil = 'eleve'
    filtre = "(&%s)" % ELEVE_FILTER
    must_args = ['login', 'password', 'nom', 'prenom', 'date',
    'classe', 'numero', 'civilite']
    may_args = dict(domaine='restreint',
                    quota='10',
                    profil='1',
                    shell=False,
                    change_pwd=False,
                    ine='',
                    patronyme='',
                    prenom2='',
                    int_id='',
                    entlogin=True,
                    regime='',
                    rattachid='',
                    enseignements='',
                    entpersonjointure='',
                    syncpassword=True,
                    )
    has_samba = True
    has_ftp = True

    def get_smbldap_useradd_args(self, login, profil, shell, classe, etab, **args):
        """
            ajoute un utilisateur à l'aide /usr/sbin/smbldap-useradd
        """
        if shell:
            shell = "bash"
        else:
            shell = "false"
        groups = '%s,%s,DomainUsers' % (classe, self._get_niveau(classe))
        if etab is not None:
            groups = 'eleves-%s,%s,%s' % (etab, etab, groups)
        useradd_args = ['-o', 'ou=local,ou=eleves',
                        '-s', '/bin/%s' % shell,
                        '-d', '/home/%s/%s' % (login[0], login),
                        '-C', '\\\\{0}\\{1}\\perso'.format(SMB_SERVEUR, login),
                        '-F', '%s' % gen_profil(profil, login),
                        '-g', 'eleves',
                        '-G', groups,
                        login]
        return useradd_args

    def _add_scribe_user(self, login, **args):
        """
            ajout les attributs Scribe à un Eleve
        """
        mail = "{0}@{1}".format(login, MAIL_DOMAIN[args['domaine']])
        maildir = self.get_maildir(login)
        args['niveau'] = self._get_niveau(args['classe'])
        datas = []
        user_dn = self.get_user_dn(login) #USER_DN % dict(uid=login, _type=self._type)
        objectclass = self._get_attr(login, 'objectClass')
        objectclass.extend(['Eleves', 'ENTPerson', 'ENTEleve', 'radiusprofile'])
        datas.append((MOD_REPLACE, 'objectClass', objectclass))
        datas.extend(gen_common_attrs(login, entprofil=self.profil, **args))
        datas.append((MOD_REPLACE, 'employeeNumber', args['numero']))
        if not_empty(args, 'ine'):
            datas.append((MOD_REPLACE, 'Ine', args['ine'] ))
        datas.append((MOD_REPLACE, 'mail', mail))
        datas.append((MOD_REPLACE, 'mailHost', 'localhost'))
        datas.append((MOD_REPLACE, 'mailDir', maildir))
        datas.append((MOD_REPLACE, 'Divcod', args['classe']))
        datas.append((MOD_REPLACE, 'Meflcf', args['niveau']))
        datas.append((MOD_REPLACE, 'userPassword', args['password']))
        #push(@mods,'rne'=>"$rne");
        datas.extend(gen_enteleve_attrs(**args))
        datas.extend(gen_radius_attrs())
        self.ldap_admin._modify(user_dn, datas)
        send_first_mail(mail)

    def _add_perso(self, login, **args):
        """
            Crée les différents répertoires de l'utilisateur
        """
        abc = join(HOME_PATH, login[0])
        rep = join(abc, login)
        perso = join(rep, 'perso')
        # Eole-AD
        create_ad_home(login, rep)
        if not isdir(perso):
            makedirs(perso, 0o700)
        if WRITE_PRIVE:
            prive = join(perso, 'prive')
            if not isdir(prive):
                makedirs(prive, 0o700)
        # répertoire supérieur
        rep = join(AD_HOME_PATH, login)
        clear_acl(rep)
        set_user_acl(rep, login, 'rwx', recursive=False)
        copy_default_acl(rep, recursive=False)
        # les profs passent et ce n'est pas hérité (#2722)
        set_group_acl(rep, 'professeurs', '--x', recursive=False)
        # chown pour la prise en compte des quotas
        set_owner(perso, login)
        if 'quota' in args:
            set_quota(login, args['quota'])
        # acl utilisateur pour assurer l'héritage
        set_user_acl(perso, login, 'rwx')
        set_group_acl(perso, 'professeurs', 'r-x')
        copy_default_acl(perso)
        if WRITE_PRIVE:
            # droits sur privé
            set_group_acl(prive, 'professeurs', '---')
            set_default_acl(prive, 'g:professeurs:---')
        # lien direct depuis profs-classe
        self.add_link_class(login, args['classe'], perso)

    def _gen_devdir(self, login, homedir=None):
        """
        Génération du répertoire "devoirs" pour les élèves
        """
        if homedir is None:
            homedir = self._get_attr(login, 'homeDirectory')[0].strip()
        gen_common_devdir(login, homedir)
        # pas de partie spécifique pour les élèves

    def _change_classe(self, user, new_classe, new_etab=None, sync=True):
        """
        Changement de classe
        @param user : login de l'élève
        @param new_classe : nouvelle classe de l'élève
        """
        old_classe = self._get_attr(user, 'Divcod')[0]
        old_niveau = self._get_attr(user, 'Meflcf')[0]
        new_niveau = self._get_niveau(new_classe)
        # modification utilisateur
        self._set_attr(user, 'Divcod', new_classe)
        self._set_attr(user, 'Meflcf', new_niveau)
        # attributs ENT (à revoir ?)
        self._set_attr(user, 'ENTEleveClasses', "%s$%s" % (BRANCHE_ETAB2 % {'etab': new_etab}, new_classe))
        self._set_attr(user, 'ENTEleveMEF', new_niveau)
        self._set_attr(user, 'ENTEleveLibelleMEF', new_niveau)
        self._touch(user)
        # désinscriptions
        self._desinscription(user, old_classe, sync=False)
        self._desinscription(user, old_niveau, sync=False)
        if new_etab is not None:
            old_etab = self.get_etab(user)
            if new_etab != old_etab:
                old_dn = self.get_user_dn(user)
                new_dn = self.get_user_dn(user, force_etab=new_etab)
                #desinscription du groupe etablissement
                self._desinscription(user, old_etab, sync=False)
                self._desinscription(user, 'eleves-' + old_etab, sync=False)
                #copie de l'utilisateur + suppression
                uidfilter = "(&%s(uid=%s))" % (USER_FILTER, user)
                cur_ldif = self.ldap_admin._search_one(uidfilter)
                self.ldap_admin._delete(old_dn)
                self.ldap_admin._add(new_dn, modlist.addModlist(cur_ldif))
                #Suppression du cache
                self.cache_etab['login'].pop(user)
                #inscription dans le groupe du nouvel etablissement
                self._inscription(user, new_etab, sync=False, etab=num_etab)
                self._inscription(user, 'eleves-' + new_etab, sync=False, etab=new_etab)
        # inscriptions
        self._inscription(user, new_classe, sync=False, etab=new_etab)
        self._inscription(user, new_niveau, sync=False, etab=new_etab)
        if sync:
            # mise à jour du ".ftp"
            self._gen_ftpdir(user)
            self._gen_groupesdir(user)
        return True

    def get_perso(self, login):
        return join(HOME_PATH, login[0], login, 'perso')

    def _inscription(self, login, groupe, sync=True, etab=None, check_etab=True, touch=True):
        """
        réécriture de l'inscription
        pour prendre en compte les liens "Option"
        """
        # appel à la classe mère
        User._inscription(self, login, groupe, sync=sync, etab=etab, check_etab=check_etab, touch=touch)
        # gestion des cas Option et Classe
        opt_dir = join(OPT_PATH, groupe)
        perso = self.get_perso(login)
        if isdir(opt_dir):
            add_symlink(perso, join(opt_dir, login))
        self.add_link_class(login, groupe, perso)

    def add_link_class(self, login, klass, perso):
        cl_dir = join(CLASS_PATH, klass)
        add_dir(cl_dir)
        if REVERSE_LOGIN:
            rev_login = get_reverse_login(login)
        else:
            rev_login = login
        add_symlink(perso, join(cl_dir, rev_login))

    def _desinscription(self, login, groupe, sync=True, touch=True):
        """
        réécriture de la désinscription
        pour prendre en compte les liens "Option"
        """
        # appel à la classe mère
        User._desinscription(self, login, groupe, sync=sync, touch=touch)
        # gestion des cas Option et Classe
        user_opt_dir = join(OPT_PATH, groupe, login)
        user_cl_dir = join(CLASS_PATH, groupe, login)
        for directory in [user_opt_dir, user_cl_dir]:
            if isdir(directory):
                unlink(directory)

    def _update(self, login, **args):
        """
            Mise à niveau Eleve (import)
        """
        user_dn = self.get_user_dn(login, force_etab=args.get('etab')) #USER_DN % dict(uid=login, _type=self._type)
        datas = []
        if not_empty(args, 'ine'):
            datas.append((MOD_REPLACE, 'Ine', args['ine'] ))
        if not_empty(args, 'int_id'):
            datas.append((MOD_REPLACE, 'intid', args['int_id'] ))
        if not_empty(args, 'entpersonjointure'):
            datas.append((MOD_REPLACE, 'ENTPersonJointure', args['entpersonjointure']))
        if not_empty(args, 'rattachid'):
            datas.append((MOD_REPLACE, 'ENTEleveStructRattachId', args['rattachid']))
        if not_empty(args, 'regime'):
            # FIXME : codes Sconet !
            datas.append((MOD_REPLACE, 'ENTEleveRegime', args['regime']))
        if not_empty(args, 'enseignements') and args['enseignements'] != []:
            datas.append((MOD_REPLACE, 'ENTEleveEnseignements', args['enseignements']))
        if not_empty(args, 'elevegroupes') and args['elevegroupes'] != []:
            datas.append((MOD_REPLACE, 'EleveGroupes', args['elevegroupes']))
        datas.append((MOD_REPLACE, 'sn', args['nom']))
        datas.append((MOD_REPLACE, 'givenName', args['prenom']))
        datas.append((MOD_REPLACE, 'cn', "%(prenom)s %(nom)s" % args ))
        datas.append((MOD_REPLACE, 'displayName', "%(prenom)s %(nom)s" % args ))
        datas.append((MOD_REPLACE, 'gecos', replace_cars("%(prenom)s %(nom)s" % args) ))
        date = deformate_date(args['date'])
        datas.append((MOD_REPLACE, 'dateNaissance', date))
        datas.append((MOD_REPLACE, 'ENTPersonDateNaissance', date))
        datas.append((MOD_REPLACE, 'LastUpdate', format_current_date()))
        self.ldap_admin._modify(user_dn, datas)

    def _Upgrade(self, login):
        """
        Mise à niveau d'un compte élève
        """
        user_dn = self.get_user_dn(login) #USER_DN % dict(uid=login, _type=self._type)
        datas = []
        obj = self._get_attr(login, 'objectClass')
        obj.extend(['ENTPerson', 'ENTEleve'])
        datas.append((MOD_REPLACE, 'objectClass', obj))
        datas.extend(self._gen_upgrade_attrs(login))
        if 'radiusprofile' not in obj:
            obj.append('radiusprofile')
            datas.extend(gen_radius_attrs())
        self.ldap_admin._modify(user_dn, datas)

    def _gen_upgrade_attrs(self, login):
        """
        Gestion des nouveaux attributs élève
        """
        infos = self._get_attrs(login, ['givenName', 'sn', 'Divcod',
                          'codecivilite', 'dateNaissance', 'Meflcf'])
        prenom = infos['givenName'][0]
        nom = infos['sn'][0]
        classe = infos['Divcod'][0]
        civilite = infos['codecivilite'][0]
        niveau = infos['Meflcf'][0]
        date = infos['dateNaissance'][0]
        attrs = []
        yesterday = format_old_date()
        attrs.append((MOD_REPLACE, 'LastUpdate', yesterday))
        attrs.append((MOD_REPLACE, 'gecos', replace_cars("%s %s" % (prenom, nom)) ))
        attrs.append((MOD_REPLACE, 'ENTPersonLogin', login))
        attrs.append((MOD_REPLACE, 'ENTPersonJointure', 'ENT'))
        attrs.append((MOD_REPLACE, 'ENTPersonProfils', self.profil))
        attrs.append((MOD_REPLACE, 'ENTPersonNomPatro', nom))
        if civilite == '3':
            sexe = 'F'
            titre = 'Mlle'
        elif civilite == '2':
            sexe = 'F'
            titre = 'Mme'
        else:
            titre = 'M.'
            sexe = 'M'
        attrs.append((MOD_REPLACE, 'ENTPersonSexe', sexe))
        attrs.append((MOD_REPLACE, 'personalTitle', titre))
        attrs.append((MOD_REPLACE, 'ENTPersonDateNaissance', fixdate(date)))
        attrs.extend(gen_enteleve_attrs(niveau=niveau, classe=classe))
        return attrs

    def _get_eleves(self, attrs=['uid']):
        """
            renvoie les élèves
        """
        return self._get_users(filtre=self.filtre, attrs=attrs)

    def is_eleve(self, uid):
        """
            test si l'utilisateur existe dans l'annuaire
            (mode déconnecté)
        """
        self.ldap_admin.connect()
        res = self._is_eleve(uid)
        self.ldap_admin.close()
        return res

    def _is_eleve(self, uid):
        """
            test si l'utilisateur existe dans l'annuaire
        """
        uidfilter = "(&%s(uid=%s))" % (ELEVE_FILTER, uid)
        if self.ldap_admin._search_one(uidfilter):
            return True
        return False

    def _delete(self, login, remove_data=False, need_connect=False, delete_resp=False):
        """
            surcharge de la méthode _delete
            pour gérer les associations élève-responsable
        """
        from scribe.responsables import Responsable
        resp = Responsable()
        if need_connect:
            resp.ldap_admin.connect()
        else:
            resp.ldap_admin = self.ldap_admin
        responsables = resp._get_responsables(['uid', 'eleve'], login)
        if delete_resp:
            # suppression si nécessaire
            for responsable in responsables:
                if len(responsable['eleve']) == 1:
                    resp._delete(responsable['uid'][0], remove_data=remove_data, need_connect=False)
                else:
                    resp.c_del_association(responsable['uid'][0], login)
        else:
            # pas de suppression -> vérification préalable
            for responsable in responsables:
                if len(responsable['eleve']) == 1:
                    raise Exception("Suppression impossible : %s est le dernier élève du responsable %s" % (login, responsable['uid'][0]))
            for responsable in responsables:
                resp.c_del_association(responsable['uid'][0], login)
        if need_connect:
            resp.ldap_admin.close()
        # ménage dans les liens symboliques
        eleve_links = glob('%s/*/%s' % (CLASS_PATH, login))
        eleve_links.extend(glob('%s/*/%s' % (OPT_PATH, login)))
        for link in eleve_links:
            unlink(link)
        # appel à la classe mère
        User._delete(self, login, remove_data, need_connect)

