# -*- coding: utf-8 -*-

import sys
from pyeole.process import system_code, system_out
from pyeole.bareos import BAREOS_CONF, BAREOS_SUPPORT, \
        MOUNT_POINT, mount_bareos_support
from eoledb.eoledbconnector import EoleDbConnector
from pyeole.ansiprint import print_red, print_orange
from creole.config import configeol
from os import system, unlink, stat, listdir
from creole.client import CreoleClient, CreoleClientError
from os.path import isfile, dirname, basename, normpath, join as joinpath
from tempfile import mkstemp
from shutil import move
import re

creole_client = CreoleClient()

catalog_label = '-catalog-'
catalog_pool_name_tmpl = '{0}' + catalog_label
catalog_db_file = '/var/lib/bareos/bareos.db'
db_type_file = '/etc/eole/bareos_type.conf'
restore_file = '/root/zephir-restore.eol'


def get_catalog_sql_file():
    return joinpath(creole_client.get_creole('bareos_db_extract_dir'), 'bareos.sql')

# Bareos may speek french (or not) (#7464)
cd_regex_en = re.compile(r'cd (?P<cd>.*?)\n(?P<invalide>Invalid path given.\n)?cwd is: (?P<cwd>.*?)\n')
cd_regex_fr = re.compile(r'cd (?P<cd>.*?)\n(?P<invalide>Invalid path given.\n)?Le répertoire courant est : (?P<cwd>.*?)\n')

def bareos_active():
    return creole_client.get_creole('activer_bareos_dir') == 'oui'

def bareos_create_restore_log_file():
    filename = '/var/log/bareos/restore.txt'
    fh = open(filename, 'w')
    fh.write('')
    fh.close()
    print ("""La restauration est lancée en tâche de fond.
Vous pouvez suivre son évolution dans le fichier {0}.
Attention, un délai est nécessaire avant l'écriture des messages dans le fichier""".format(filename))

def bareos_restore_one_file(filename, jobid):
    bconsole_command("file={0} done\n{1}\n".format(filename, jobid), stdout_param="@output /dev/null\n")
    bareos_create_restore_log_file()

def bareos_ls_one_folder(foldername, jobid, test_mode=False):
    global cd_regex
    foldername = normpath(foldername)
    if foldername[0] != "/":
        print_orange("Un chemin complet est requis")
        if test_mode == True:
            return False
        else:
            foldername = "/" + foldername
            print_orange("Essai avec {0}".format(foldername))
    stdout = bconsole_command("\ncd {0}\nls\ndone\n".format(foldername)).split("$ ")[1:]
    if stdout == []:
        print_red("Erreur lors de la récupération du catalogue")
        sys.exit(1)
    ls_result = stdout[1].split('\n')[1:]
    cd_result = re.search(cd_regex_en, stdout[0])
    if cd_result is None:
        cd_result = re.search(cd_regex_fr, stdout[0])
        if cd_result is None:
            print_red("Réponse de Bareos non reconnue")
            print ('"""\n{0}"""'.format(stdout[0]))
            sys.exit(1)
    if cd_result.groupdict()['invalide'] == None:
        if test_mode == True:
            return True
        else:
            print ('\n'.join(ls_result))
    else:
        print_red("Le repertoire {0} n'existe pas.".format(foldername))
        if test_mode == True:
            return False
        else:
            foldername = dirname(foldername)
            print ("liste du contenu du repertoire parent {0} :".format(foldername))
        bareos_ls_one_folder(foldername, jobid)

def bareos_restore_one_folder(foldername, jobid, exit=True):
    foldername = normpath(foldername)
    if not bareos_ls_one_folder(foldername, jobid, test_mode=True) == True:
        if exit:
            sys.exit(0)
    else:
        parent = dirname(foldername)
        dirn = basename(foldername)
        bconsole_command("\ncd {0}\nmark {1}\ndone\n{2}\n".format(parent, dirn,
            jobid), stdout_param="@output /dev/null\n")
        bareos_create_restore_log_file()

def bareos_restore_all_files(jobid):
    bconsole_command('\ndone\n{0}\n'.format(jobid), prefix='select current all yes', stdout_param="@output /dev/null\n")
    bareos_create_restore_log_file()

def bareos_mark_path(path_to_mark):
    path_to_mark = normpath(path_to_mark)
    parent = dirname(path_to_mark)
    target = basename(path_to_mark)
    return '\ncd {0}\nmark {1}'.format(parent, target)

def bareos_mark_paths(path_list, jobid):
    mark = ''
    for foldername in path_list:
        mark += bareos_mark_path(foldername)
    return mark

def bareos_restore_several_paths(path_list, jobid):
    mark_chain = bareos_mark_paths(path_list, jobid)
    if mark_chain:
        bconsole_command("{0}\ndone\n{1}\n".format(mark_chain,
            jobid), stdout_param="@output /dev/null\n")
        bareos_create_restore_log_file()

def bareos_restore(jobid, selection='', prefix='select current yes', stdout_param="@output /dev/null\n"):
    cmd = '{0}\ndone\n{1}\n'.format(selection, jobid)
    bconsole_command(cmd, prefix=prefix, stdout_param=stdout_param)
    bareos_create_restore_log_file()

def bareos_has_catalog():
    print('bareos_has_catalog', EoleDbConnector({'dbname': 'bareos'}).__dbExists__())
    return EoleDbConnector({'dbname': 'bareos'}).__dbExists__()

def bareos_restore_dump():
    catalog_sql_file = get_catalog_sql_file()
    system_code(['/usr/share/eole/sbin/bareosregen.sh', catalog_sql_file])

def bareos_search(filenames):
    if type(filenames) != list or filenames == []:
        raise Exception('Il faut une liste de fichier pour bareos_search')
    cmd = '\n@output\nfind ' + ' '.join(filenames)
    stdout = bconsole_command(cmd)
    try:
        stdout = stdout.split('$')[1].split('\n')[2:]
    except IndexError:
        raise Exception("Erreur à la recherche du fichier : {0}\nLe retour de la commande bconsole : {1}".format(filenames, stdout))
    stdout = [f for f in stdout if f != '']
    if len(stdout) > 0:
        print ('\n'.join(stdout))
    else:
        print_orange(u'Aucun résultat')

def bconsole_command(cmd, prefix='select current yes', stdout_param=""):
    if not bareos_active():
        raise Exception("Bareos n'est pas actif, impossible de restaurer")
    bareos_fd = creole_client.get_creole('bareos_fd_name')
    code, stdout, stderr = system_out(["bconsole", "-c", "/etc/bareos/bconsole.conf"], stdin="{3}restore FileSet=FileSetSauvegarde Client={0} {1} {2}\n".format(bareos_fd, prefix, cmd, stdout_param))
    if code != 0:
        raise Exception('Erreur bconsole')
    return stdout

def get_volume_name(bareos_dir_name):
    try:
        mount_bareos_support()
    except:
        pass
    volume_names = [v for v in listdir(MOUNT_POINT) if catalog_label in v]
    if len(volume_names) > 0:
        filtered_volume_names = [v for v in volume_names if catalog_pool_name_tmpl.format(bareos_dir_name) in v]
        if len(filtered_volume_names) > 0:
            return max([(stat(joinpath(MOUNT_POINT, i)).st_mtime, i)
                for i in filtered_volume_names])[1]
        else:
            print_red("échec de la restauration")
            print ("\n".join(["Les noms de directeur possibles sont :"] + ["\t" + v.split(catalog_label)[0] for v in volume_names]))
            sys.exit(1)
    else:
        print_red("échec de la restauration")
        print ("Aucun volume respectant le modèle de nommage dans le répertoire {0}".format(MOUNT_POINT))
        sys.exit(1)

def extract_bareos_files():
    bareos_dir_name = creole_client.get_creole('bareos_dir_name')
    volume_name = get_volume_name(bareos_dir_name)
    catalog_sql_file = get_catalog_sql_file()

    bsr_name = '/var/lib/bareos/{0}-JobDefsSauvegarde.bsr'.format(bareos_dir_name)
    extract_file(bsr_name, bareos_dir_name, volume_name=volume_name)
    if isfile(bsr_name):
        system_code(['chown', 'bareos:bareos', bsr_name])
    bsr_name = '/var/lib/bareos/{0}-JobDefsCatalog.bsr'.format(bareos_dir_name)
    extract_file(bsr_name, bareos_dir_name, error_if_not_found=False, volume_name=volume_name)
    if isfile(bsr_name):
        system_code(['chown', 'bareos:bareos', bsr_name])
    extract_file(BAREOS_SUPPORT, bareos_dir_name, error_if_not_found=False, volume_name=volume_name)
    extract_file(BAREOS_CONF, bareos_dir_name, error_if_not_found=False, volume_name=volume_name)
    extract_file(catalog_sql_file, bareos_dir_name, volume_name=volume_name)

def extract_configeol_files(bareos_dir_name=None):
    if bareos_dir_name == None:
        bareos_dir_name = creole_client.get_creole('bareos_dir_name')

    system_code(['/bin/sed', '-i',
            's@  Archive Device = /var/lib/bareos/storage@  Archive Device = {0}@g'.format(MOUNT_POINT),
            '/etc/bareos/bareos-sd.d/device/FileStorage.conf'])
    volume_name = get_volume_name(bareos_dir_name)
    extract_file(configeol, bareos_dir_name, dest_file=restore_file, volume_name=volume_name)
    print ('Le fichier config.eol a été restauré avec le nom {0}'.format(restore_file))
    print ('Pour que ce fichier soit pris en compte, il faut le déplacer : mv {0} /etc/eole/config.eol'.format(restore_file))

def extract_file(filename, bareos_dir_name, dest_file=None, error_if_not_found=True, volume_name=None):
    """Extract file directly in a volume identified by file name
    (bareos tools use base path name from configuration file)"""
    tmp_file = mkstemp()[1]
    include_file = open(tmp_file, 'w')
    include_file.write(filename)
    include_file.close()
    extract_dir = '/tmp'
    if volume_name is None:
        volume_name = get_volume_name(bareos_dir_name)
    code, stdout, stderr = system_out(['/usr/sbin/bls', '-V', volume_name, 'FileStorage', '-i', tmp_file])
    if code == 1:
        unlink(tmp_file)
        raise Exception('Erreur lors de bls : {0},{1}'.format(stdout, stderr))
    if stdout.strip().split('\n')[-2] == "0 files and directories found.":
        if error_if_not_found:
            print ("Pas de fichier {0} dans le volume {1}".format(filename, volume_name))
            raise Exception("Impossible de lister le catalogue {0}".format(volume_name))
    else:
        code, stdout, stderr = system_out(['/usr/sbin/bextract', '-V', volume_name, 'FileStorage', extract_dir, '-i', tmp_file])
        if code == 1:
            unlink(tmp_file)
            print ("Impossible de restaurer {0} du catalogue {1}".format(filename, volume_name))
            raise Exception(stderr)
        if dest_file == None:
            dest_file = filename
        move("{0}{1}".format(extract_dir, filename), dest_file)
    unlink(tmp_file)

def exit_if_running_jobs():
    if not bareos_active():
        raise Exception("Bareos n'est pas actif, impossible de restaurer")
    code, out, err = system_out(["/usr/bin/bconsole"], stdin='status dir')
    try:
        running_lines = out.split('\n====\n')[1]
    except:
        print ("Erreur de status")
        print (out + " " + err)
        sys.exit(1)

    if not 'No Jobs running.' in running_lines and not 'Pas de job en cours.' in running_lines:
        print ("Impossible de restaurer, jobs en cours")
        print ("\n".join(running_lines.split('\n')[5:]))
        sys.exit(1)

