import os
import sys
import logging
import argparse
from .kaomi_client import KaomiClient, ServerError
from .globals import PERMS_MASK, SERVER_PORT_DEFAULT, API_PATHS
from .functions import get_client_output, upload_directory, display_output_and_exit


def check_perms(value):
    """
    Funzione di controllo dei permessi passati via linea di comando
    Viene applicata conversione e maschera
    :param value:                   valore passato come argomento del parametro indicante i permessi
    :throw ArgumentTypeError:       nel caso il permesso non sia un numero
    :return:                        permessi validi
    """

    try:
        permissions = int(value, 8)
    except ValueError:
        # il numero passato non è convertibile in binario
        raise argparse.ArgumentTypeError('Permissions must be specified as an octal number (eg. 0o777, 0o0777, 0777, 777, etc.)')

    # maschero i permessi per eliminare configurazioni non ammissibili
    perms = permissions & PERMS_MASK

    # vedo se la maschera ha modificato i permessi
    if perms != permissions:
        print('Warning: specified permissions have been masked to {}'.format(oct(perms)))

    return perms


def get_arg_parser():
    """
    Funzione per l'inizializzazione dell'argparser da utilizzare
    :return:            istanza dell'arg_parser
    """
    parser = argparse.ArgumentParser(description="""
    Kaomi deployer client script.
    This script can be used to deploy folders, files or to execute actions on a server that runs kaomi deployer.

    """, prog="python -m kaomi_client")

    requested_action = parser.add_mutually_exclusive_group(required=True)

    # aggiunta degli argomenti da linea di comanda
    parser.add_argument('-k', '--key', type=str, dest='apikey', metavar="apikey", required=True, help="apikey to be used for authentication.")
    parser.add_argument('-s', '--server', type=str, dest='server_host', metavar="server_host", required=True, help="server host.")
    parser.add_argument('-p', '--port', type=int, dest='server_port', metavar="server_port", required=False, default=SERVER_PORT_DEFAULT, help="port of the server to be deployed (default: %(default)s)")

    requested_action.add_argument('-d', '--directory', nargs=2, dest='directory', metavar=("local_dir", "remote_dir"), help="upload directory 'local_dir' to 'remote_dir'.")
    requested_action.add_argument('-f', '--file', nargs=2,  dest='file', metavar=("local_file", "remote_file"), help="upload file 'local_file' to 'remote_file'.")
    requested_action.add_argument('-a', '--action', dest='action', metavar="action_name", help="action to be executed")
    requested_action.add_argument('-x', '--delete', dest='delete', metavar="path", help="delete specified file or directory")

    parser.add_argument('-dp', '--dir-perms', type=check_perms, dest='dir_perms', metavar="permissions", required=False, help="permissions to be applied to uploaded folders (default: root folder ones)")
    parser.add_argument('-fp', '--file-perms', type=check_perms, dest='file_perms', metavar="permissions", required=False, help="permissions to be applied to uploaded files (default: root folder ones)")
    parser.add_argument('-o', '--overwrite', dest='overwrite_flag', action='store_true', required=False, help="specifies if folder/file that already exists on server should be deleted or not (default: false)")
    parser.add_argument('-v', '--verbose', dest='verbose_flag', action='store_true', required=False, help="verbose output (default: false)")

    return parser


##########################
########## MAIN ##########
##########################

# logging configuration
logging.basicConfig(filename="client_errors.log", format="[%(asctime)s][%(levelname)s][%(lineno)d][%(funcName)s] %(message)s", level=logging.INFO)

# inizializzo il parser per gli argomenti via linea di comando
parser = get_arg_parser()
args = parser.parse_args(sys.argv[1:])

# provo ad aprire una connessione con il server
try:
    kaomi_client = KaomiClient(server_host=args.server_host, server_port=args.server_port, apikey=args.apikey, api_paths=API_PATHS)
except ConnectionError as e:
    display_output_and_exit({'status': 2, 'message': 'Error establishing new connection', 'data': 'ConnectionError: {}'.format(e)})

# il funzionamento del main risente delle modifiche
if args.action is not None:
    # è stata richiesta l'esecuzione di un'azione
    try:
        # noinspection PyUnboundLocalVariable
        server_resp = kaomi_client.execute_action(action_name=args.action)
    except ConnectionError as e:
        display_output_and_exit({'status': 2, 'message': 'ConnectionError executing action', 'data': 'ConnectionError: {}'.format(e)})
    except RuntimeError as e:
        display_output_and_exit({'status': 1, 'message': 'RuntimeError executing action', 'data': 'RuntimeError: {}'.format(e)})
    # non ci sono stati errori nella richiesta, analizzo cosa ho ottenuto
    # noinspection PyUnboundLocalVariable
    display_output_and_exit(client_output=get_client_output(server_output=server_resp), verbose_flag=args.verbose_flag)

elif args.file is not None:
    # è stato richiesto l'upload di un file

    # ottengo il path del file di origine e quello del file di destinazione
    local_file, remote_file = args.file

    # come prima cosa controllo che il file locale esista
    if not os.path.isfile(local_file):
        # il file che si vuole uploadare non esiste, allora ritorno errore
        display_output_and_exit({'status': 1, 'message': 'Cannot find selected file', 'data': 'File: {}'.format(local_file)})

    # se il file esiste procedo al deployment
    try:
        # noinspection PyUnboundLocalVariable
        server_resp = kaomi_client.upload_file(local_file=local_file, dest_file=remote_file, overwrite=args.overwrite_flag, perms=args.file_perms)
    except ConnectionError as e:
        display_output_and_exit({'status': 2, 'message': 'ConnectionError uploading file', 'data': 'ConnectionError: {}'.format(e)})
    except RuntimeError as e:
        display_output_and_exit({'status': 1, 'message': 'RuntimeError uploading file', 'data': 'RuntimeError: {}'.format(e)})

    # non ci sono stati errori nella richiesta, analizzo cosa ho ottenuto
    # noinspection PyUnboundLocalVariable
    display_output_and_exit(client_output=get_client_output(server_output=server_resp), verbose_flag=args.verbose_flag)

elif args.directory is not None:
    # è stato richiesto l'upload di una directory

    local_dir, remote_dir = args.directory

    if not os.path.isdir(local_dir):
        # la directory che si vuole uploadare non esiste, allora ritorno errore
        display_output_and_exit({'status': 1, 'message': 'Cannot find selected directory', 'data': 'Dir: {}'.format(local_dir)})

    # come prima cosa provo a creare la cartella sul server in modo da ottenere eventuali errori
    # ad esempio se overwrite = false e la cartella esiste già, lo si comunica all'utente
    try:
        # noinspection PyUnboundLocalVariable
        server_resp = kaomi_client.create_directory(dest_dir=remote_dir, overwrite=args.overwrite_flag, perms=args.dir_perms)
    except ConnectionError as e:
        display_output_and_exit({'status': 2, 'message': 'ConnectionError uploading folder', 'data': 'ConnectionError: {}'.format(e)})
    except RuntimeError as e:
        display_output_and_exit({'status': 1, 'message': 'RuntimeError uploading folder', 'data': 'RuntimeError: {}'.format(e)})

    # procedo solo se la creazione della cartella ha avuto successo
    # noinspection PyUnboundLocalVariable
    if server_resp.get('status') != 0:
        # la creazione della cartella non ha avuto successo, allora esco
        display_output_and_exit(client_output=get_client_output(server_output=server_resp), verbose_flag=args.verbose_flag)

    try:
        # se la cartella è stata creata corretamente allora provo a caricare il suo contenuto
        upload_directory(kaomi_client=kaomi_client, local_dir=local_dir, remote_dir=remote_dir, dir_perms=args.dir_perms,
                         file_perms=args.file_perms, verbose=args.verbose_flag)
    except ConnectionError as e:
        display_output_and_exit({'status': 2, 'message': 'ConnectionError uploading folder. Folder on server might be inconsistent!', 'data': 'ConnectionError: {}'.format(e)})
    except RuntimeError as e:
        display_output_and_exit({'status': 1, 'message': 'RuntimeError uploading folder. Folder on server might be inconsistent', 'data': 'RuntimeError: {}'.format(e)})
    except ServerError as exc:
        server_error = exc.args[0]
        display_output_and_exit(get_client_output(server_output=server_error))

    display_output_and_exit({'status': 0, 'message': 'Operation concluded successfully', 'data': ''}, args.verbose_flag)

elif args.delete is not None:
    # è stata richiesta l'eliminazione di un file o una cartella
    file_to_delete = args.delete

    try:
        # noinspection PyUnboundLocalVariable
        server_resp = kaomi_client.delete_filesystem_node(path=file_to_delete)
    except ConnectionError as e:
        display_output_and_exit({'status': 2, 'message': 'ConnectionError deleting node', 'data': 'ConnectionError: {}'.format(e)})
    except RuntimeError as e:
        display_output_and_exit({'status': 1, 'message': 'RuntimeError deleting node', 'data': 'RuntimeError: {}'.format(e)})

    # noinspection PyUnboundLocalVariable
    display_output_and_exit(client_output=get_client_output(server_output=server_resp), verbose_flag=args.verbose_flag)
else:
    # non è stato riconosciuto cosa è stato richiesto
    display_output_and_exit({'status': 1, 'message': 'Client called without an operation'})