#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Ce script sert dans le cadre de l'importation AAF.
# Il créer les répertoires utilisateurs associés au serveur local


from optparse import OptionParser
from os import path, makedirs
from sys import exit, argv
#from samba.samdb import SamDB
#from samba.auth import system_session
#from samba import getopt
#import ldb
from shutil import move
from os import listdir
from configobj import ConfigObj

from scribe.eleves import Eleve
from scribe.enseignants import Enseignant
from scribe.administratifs import Administratif
from scribe.eolegroup import Group
from scribe import eoleshare  # import Share
from scribe.ldapconf import USERS_DN, SMB_SERVEUR, GROUP_PATH, CLASS_PATH, OPT_PATH, AD_HOME_PATH, RECYCLAGE_PATH, SHARE_FILTER
from creole.client import CreoleClient
from pyeole.process import creole_system_code, creole_system_out
from datetime import datetime


conffile = '/etc/synchro_aaf.conf'
if not path.isfile(conffile):
    exit("/!\\ Impossible de trouver le fichier de configuration, avez-vous activé la création des répertoires locaux ? /!\\")
config = ConfigObj(conffile)
SYNCHRO_TYPE = config.get('synchro_type', 'all')
QUOTA_STUDENT = int(config.get('quota_student', 0))
QUOTA_STUDENT_HARD = int(config.get('quota_student_hard', 0))
QUOTA_TEACHER = int(config.get('quota_teacher', 0))
QUOTA_TEACHER_HARD = int(config.get('quota_teacher_hard', 0))
QUOTA_ADMINISTRATIVE = int(config.get('quota_administrative', 0))
QUOTA_ADMINISTRATIVE_HARD = int(config.get('quota_administrative_hard', 0))
NOM_DOMAINE_MACHINE = config['nom_domaine_machine']


exit_code = 0
if '--debug' in argv:
    DEBUG = True
else:
    DEBUG = False

# Optimization to avoid a query every time we call set_quota
creole_client = CreoleClient()
file_container = creole_client.get_container('fichier')


# Copy of functions from fichier.quota but optimized
def get_quota(loin):
    cmd = ['/usr/bin/quota', '-w', login]
    res = creole_system_out(cmd, container=file_container)[1].splitlines()
    if len(res) < 3:
        user_quota = 0
    else:
        nb_blocks = res[2].split()[2]
        # on retourne la limite en Mo
        user_quota = int(int(nb_blocks)/1024)
    return user_quota


def set_quota(login,
              quota,
              hard_quota,
              ):
    cmd = ['/usr/sbin/quotatool', '-b',
           '-q', '%dM' % quota,
           '-l', '%dM' % hard_quota,
           '-u', login, '/home']
    return creole_system_code(cmd, container=file_container)


def archivage(type_, src_directory, name):
    srcdir = path.join(src_directory, name)
    if not path.isdir(srcdir):
        return
    print("Archivage de {} ({})".format(name, type_))
    subdestdir = path.join(RECYCLAGE_PATH, str(datetime.today().year), type_)
    if not path.isdir(subdestdir):
        makedirs(subdestdir)
    destdir = path.join(subdestdir, name)
    if path.isdir(destdir):
        print('!!! une archive "{}" existe déjà, archivage "{}" ({}) annulé !!!'.format(destdir, srcdir, type_))
        global exit_code
        exit_code = 1
    else:
        move(srcdir, destdir)


if DEBUG:
    print('debug de la synchronisation')

# Instanciation des objects de manipulation des fiches LDAP Scribe
meta_eleve = Eleve()
meta_eleve.ldap_admin.connect()
meta_enseignant = Enseignant()
meta_administratif = Administratif()
meta_group = Group()
meta_share = eoleshare.Share()
search = meta_eleve.ldap_admin._search

squery = "objectClass=sambaFileShare"
local_etabs = set()
local_global_groups_info = set()
server_pattern = '\\\\' + NOM_DOMAINE_MACHINE + '\\'
for share in search(squery):
    if not 'uNCName' in share[1] or not share[1]['uNCName'][0].lower().startswith(server_pattern):
        continue

    if 'rne' in share[1]:
        local_etabs.add(share[1]['rne'][0])
    else:
        # Probably a global group
        local_global_groups_info.add((str(share[1]['sambaShareName'][0]), 'etablissement'))
if DEBUG:
    print(f' * les établissements locaux {list(local_etabs)}')

etab_ids = '(|' + ''.join(['(rne=' + local_etab + ')' for local_etab in local_etabs]) + ')'
eoleshare.SHARE_FILTER = '(&' + SHARE_FILTER + etab_ids + ')'

# Gestion des classes :
# - recherche des classes a créer localement
# - créer les répertoires
# - supprimer les classes non retrouvées dans l'annuaire
cquery = "(&(objectClass=classe){})".format(etab_ids)
local_classes = []
local_etabs = set()
if SYNCHRO_TYPE in ['peda', 'all']:
    for classe in search(cquery):
        local_classes.append(str(classe[1]['cn'][0]))
        local_etabs.add(classe[1]['rne'][0])
    if DEBUG:
        print(' * classes locales {}'.format(cquery))
    for name in local_classes:
        if DEBUG:
            print('  - {}'.format(name))
        meta_share._make_dirs(name, path.join(GROUP_PATH, name), 'classe')
    if path.isdir(CLASS_PATH):
        for classe in set(listdir(CLASS_PATH)) - set(local_classes):
            archivage('classe', CLASS_PATH, classe)

# Gestion des options :
# - recherche des options a créer localement
# - créer les répertoires
# - supprimer les options nont retrouvées dans l'annuaire
oquery = "(&(objectClass=eolegroupe)(type=option){})".format(etab_ids)
if SYNCHRO_TYPE in ['peda', 'all']:
    local_options = [str(option[1]['cn'][0]) for option in search(oquery)]
    if DEBUG:
        print(' * options local {}'.format(oquery))
    for name in local_options:
        if DEBUG:
            print('  - {}'.format(name))
        meta_share._make_dirs(name, path.join(GROUP_PATH, name), 'option')
    if path.isdir(OPT_PATH):
        for option in set(listdir(OPT_PATH)) - set(local_options):
            archivage('option', OPT_PATH, option)
else:
    local_options = []

# Gestion des équipes :
# - recherche des équipes a créer localement
# - créer les répertoires
equery = "(&(objectClass=eolegroupe)(type=equipe){})".format(etab_ids)
if SYNCHRO_TYPE in ['peda', 'all']:
    local_equipes = [str(equipe[1]['cn'][0]) for equipe in search(equery)]
    if DEBUG:
        print(' * equipes local {}'.format(equery))
    for name in local_equipes:
        if DEBUG:
            print('  - {}'.format(name))
        meta_share._make_dirs(name, path.join(GROUP_PATH, name), 'rw')
else:
    local_equipes = []

# Recherche des utilisateurs locaux et des groupes locaux
# - créer les répertoires des groupes
gquery = "(&(|(type=Etablissement)(type=Rw)(type=Ro)(type=Dt)){})".format(etab_ids)
local_users = dict()
local_etab_groups_info = []
for group in search(gquery):
    name = str(group[1]['cn'][0])
    typ_ = str(group[1]['type'][0]).lower()
    if SYNCHRO_TYPE == 'peda' and '_administratifs' in name:
        continue
    if SYNCHRO_TYPE == 'admin' and '_administratifs' not in name:
        continue

    for user_info in search("(memberOf={})".format(group[0])):
        user_dn = user_info[0]
        local_users[user_dn] = user_info[1]
    local_etab_groups_info.append((name, typ_))
local_etab_groups = []
if DEBUG:
    print(' * groupes local {}'.format(gquery))
for name, typ in local_etab_groups_info:
    if DEBUG:
        print('  - {}'.format(name))
    if typ == 'etablissement':
        typ = 'rw'
    meta_share._make_dirs(name, path.join(GROUP_PATH, name), typ)
    local_etab_groups.append(name)

# Gestion des partages globaux
for name, typ in local_global_groups_info:
    if typ == 'etablissement' and name != 'commun':
        typ = 'rw'
    meta_share._make_dirs(name, path.join(GROUP_PATH, name), typ)
    local_etab_groups.append(name)

# Purge des groupes non géré localement
if path.isdir(GROUP_PATH):
    for share in set(listdir(GROUP_PATH)) - set(local_classes) - set(local_options) - set(local_equipes) - set(local_etab_groups) - {'commun', 'professeurs', 'devoirs'}:
        archivage('groupe', GROUP_PATH, share)

meta_enseignant.ldap_admin = meta_eleve.ldap_admin
meta_administratif.ldap_admin = meta_eleve.ldap_admin
meta_share.ldap_admin = meta_eleve.ldap_admin
local_user_names = set()
if DEBUG:
    print(' * utilisateurs locaux :')
for user_dn, user_info in local_users.items():
    login = str(user_info['cn'][0])
    local_user_names.add(login)
    object_classes = [str(oc) for oc in user_info['objectClass']]
    has_quota = get_quota(login) != 0
    args = {}
    if 'Eleves' in object_classes:
        if SYNCHRO_TYPE == 'admin':
            continue
        typ = 'eleve'
        meta = meta_eleve
        user_class = str(user_info['Divcod'][0])
        args['classe'] = user_class
        quota = QUOTA_STUDENT
        hard_quota = QUOTA_STUDENT_HARD
    elif 'administrateur' in object_classes:
        if SYNCHRO_TYPE == 'admin':
            continue
        typ = 'professeur'
        meta = meta_enseignant
        quota = QUOTA_TEACHER
        hard_quota = QUOTA_TEACHER_HARD
    elif 'administratif' in object_classes:
        if SYNCHRO_TYPE == 'peda':
            continue
        typ = 'administratif'
        meta = meta_administratif
        quota = QUOTA_ADMINISTRATIVE
        hard_quota = QUOTA_ADMINISTRATIVE_HARD
    else:
        continue
    if DEBUG:
        print('  - {} ({})'.format(login, typ))

    try:
        meta._add_perso(login, **args)
        meta._gen_groupesdir(login)
        meta._gen_ftpdir(login)
        if not has_quota:
            set_quota(login,
                      quota,
                      hard_quota,
                      )
    except Exception as err:
        print('impossible de synchroniser {} ({}) : {}'.format(login, typ, err))
        exit_code = 1

for login in set(listdir(AD_HOME_PATH)) - local_user_names - {'mail'}:
    # vérifie s'il n'y a pas eu une création à la main
    query = "(&(objectClass=user)(cn={})(!(loginShell=*)))".format(login)
    if not list(search(query)):
        archivage('utilisateur', AD_HOME_PATH, login)
if DEBUG:
    print(' * création des partages samba avec le filtre {}'.format(eoleshare.SHARE_FILTER))
meta_share._synchronize()  # NOPE. We must filter on SHARE_FILTER + ucname
meta_eleve.ldap_admin.close()

if exit_code:
    print('')
    print('une erreur est survenue durant la synchronisation !')
    exit(exit_code)
