#!/usr/bin/env python3

import getpass
import datetime
import argparse
from sys import argv
import logging
import paramiko
import time


def parsing_arguments():
    parser = argparse.ArgumentParser()

    parser.add_argument('--start_date', default='2020-01-01',
                        help='Expected only date (without time) in ISO format YYYY-MM-DD'
                        )
    parser.add_argument('--stop_date',
                        help="Expected only date (without time) in ISO format YYYY-MM-DD. If not define, uses 'start_date + 1 day'."
                        )
    parser.add_argument('--time_shift',
                        help="""Time step for getting data. 
                        By default dump will be gotten from start_time to stop_time with time_shift for one day - we'll get one file per day. 
                        But if we can create requests for more data we can use time_shift for a week or month, 
                        in this case every file will be bigger, but sunnnary count of them will be lower.
                        Expected count of days.""",
                        default=1)
    parser.add_argument('--db_login', default='javauser')
    parser.add_argument('--db_password', default='javapassword')
    parser.add_argument('--host', default='localhost',
                        help='remote server address')
    parser.add_argument('--ssh_login',
                        help='username for ssh connect')
    parser.add_argument('--ssh_password',
                        help='password for ssh connect')
    parser.add_argument('--ssh_port', default=22,
                        help='port for ssh connect')

    namespace = parser.parse_args(argv[1:])

    # ну а что делать, если не хочется светить пароль для ssh?
    if not namespace.ssh_login:
        namespace.ssh_login = str(input('SSH login: '))
    if not namespace.ssh_password:
        namespace.ssh_password = getpass.getpass(prompt='SSH password: ')

    return namespace


def command_generator(db_user, db_password, current_date, next_date, file):
    """
         Команда довольно длинная, чтобы было удобней читать, собраем ее
     в виде списка, который потом склеивается джоином.

     # grep
     # сразу удаляем все ненужные нам строки, оставляем только insert данных
     #    grep -vE "^/|^--|^$|LOCK TABLES|UNLOCK TABLES"
     # (надо попробовать заменить на grep "INSERT INTO" так должно быть проще)
    grep переписан на выборку строчек с определенным количеством запятых - 39



     sed
     удаляем лишнее, исправляем неправильное
        s/INSERT INTO \`radacct\` VALUES (//g    --- удаление ненужных sql инструкций
        s/),(/\n/g      --- замена разделителей строк в запросе на перенос строки
        s/'//g    --- удалени апострофов
        s/);//g    --- удаление симовлов завершающих sql запросы

    :param db_user: логин к БД. строка
    :param db_password: пароль к БД. строка
    :param start_date_parsed: день, который будем парсить. СТРОКА!
    :param file: название файла в который будет сохраняться архив. СТРОКА!
    :return: готовая команда, которую можно кинуть в баш. строка
    """
    cmd = [""" mysqldump -u{} -p{} """.format(db_user, db_password),
           """ --quick --no-create-db --no-create-info """,
           """ "--where=(acctstarttime >= '{0}' """.format(current_date),
           """ and acctstarttime < '{0}')" """.format(next_date),
           """ radius radacct | """,
           """ sed "s/INSERT INTO \`radacct\` VALUES (//g;s/),(/\\n/g;s/'//g;s/);//g" | """,
           """ egrep "{}" | """.format('^[^,]*' + ',[^,]*' * 39 + '$'),
           """ gzip > {0} && echo 'finished'""".format(file)]

    result = ''.join(cmd)
    logging.debug("Generated command:\n{}".format(result))
    return result


if __name__ == '__main__':

    logging.basicConfig(filename='log.log',
                        format='%(asctime)s %(levelname)s %(message)s',
                        datefmt='%Y-%m-%dT%H:%M:%S.%s',
                        level=logging.INFO)

    args = parsing_arguments()
    logging.error("Start process")

    # определяе формат, который будет использвоаться для выборки данных из БД и для генерации имени файла
    date_format = '%Y-%m-%d'

    # введенная дата преобразуется в объект datetime, чтобы потом легко инкрементировать ее
    computing_date = datetime.datetime.strptime(args.start_date, date_format)

    # Сдвиг времени определяет количество данных, которые будут выгружаться из бд
    time_shift = datetime.timedelta(days=int(args.time_shift))

    # если при вызове скрипта на была указана конечная дата, то считаем, что это указанный день +1
    if not args.stop_date:
        stop_date = computing_date + time_shift
    else:
        stop_date = datetime.datetime.strptime(args.stop_date, date_format)

    # Создание SSH клиента для выполнения команд и поверх него SFTP клиента для скачивания файлов
    try:
        client = paramiko.client.SSHClient()
        client.load_system_host_keys()
        client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy)
        client.connect(hostname=args.host,
                       port=args.ssh_port,
                       username=args.ssh_login,
                       password=args.ssh_password)

        sftp_client = client.open_sftp()

    except paramiko.ssh_exception.AuthenticationException as e:
        print(e)
        logging.error("Error while establishing SSH connection: {}".format(e))
        exit(1)
    logging.debug("SSH connection established")

    while computing_date < stop_date:
        # но для составления запросов в БД дата нужна в виде строки и имя файла, который мы хотим получить
        # эти значения нужны в циклах
        current_date = computing_date.strftime(date_format)
        next_date = (computing_date + time_shift).strftime(date_format)
        message_string = "Requested date (start_date): '{}'".format(current_date)
        print(message_string)
        logging.debug(message_string)
        file = '{}.gz'.format(current_date)
        message_string = "Destination file: '{}'.".format(file)
        print(message_string)
        logging.debug(message_string)

        command = command_generator(db_user=args.db_login,
                                    db_password=args.db_password,
                                    current_date=current_date,
                                    next_date=next_date,
                                    file=file)
        message_string = "Start dumping date '{}' into file '{}'".format(current_date, file)
        print(message_string)        
        logging.info(message_string)
        stdin, stdout, stderr = client.exec_command(command)
        command_result = str(stdout.read(), 'utf-8')
        if 'finished' in command_result.split():
            message_string = "Dump for date '{}' successfully has dumped into file '{}'.".format(current_date, file)
            print(message_string)            
            logging.info("Dump for date '{}' successfully has dumped into file '{}'.".format(current_date, file))
        else:
            message = "Something was wrong when process data for date '{}' into file '{}'. Caught stderr: {}"
            message_string = message.format(current_date, file, '\n' + str(stderr.read(), 'utf-8'))
            print(message_string)
            logging.error(message_string)

        message_string = "Start downloading file '{}' from remote server.".format(file)
        print(message_string)
        logging.info(message_string)
        # попытка скачать файл и удалить его с сервера
        try:
            sftp_client.get(remotepath=file, localpath=file)
            sftp_client.remove(path=file)
            message_string = "File '{}' successfully has gotten from server.".format(file)
            print(message_string)            
            logging.info(message_string)
        except FileNotFoundError as e:
            message_string = "File '{}' has not found while processed data '{}' ".format(file, current_date)
            print(message_string)
            logging.error(message_string)

        # самое важное в цикле - инкрементировать дату!
        computing_date = computing_date + time_shift
    message_string = "Finish process"
    print(message_string)
    logging.error("Finish process")
