#!/usr/bin/env python3

"""
实现自动发现实例、自动备份、自动清理的相关功能
"""

import re
import os
import sys
import time
import json
import shutil
import psutil
import logging
import argparse
from datetime import datetime

from dbma import checkings
from dbma.initialization import is_root, get_uid_gid
from dbma.daemon import start_daemon, stop_daemon
from dbma.dbmacnf import cnf
from dbma import monitor
from dbma import backup
from dbma import common



def parser_cmd_args():
    """
    实现命令行参数的处理
    """
    name, *_ = os.path.basename(__file__).split('.')
    parser = argparse.ArgumentParser(name)
    parser.add_argument('--log', type=str, default='info',
                        choices=['debug', 'info', 'warning', 'error'])
    parser.add_argument('--log-file', type=str,
                        default="/usr/local/dbm-agent/logs/dbm-backup-proxy.log")
    parser.add_argument('--spin-time', default=500, type=int)
    parser.add_argument('action', type=str, choices=['start', 'stop'])
    args = parser.parse_args()
    return args


def config_log(log_file: str, log_level='info'):
    """
    配置日志
    """
    logger = logging.getLogger('dbm-agent')
    if log_level.upper() == 'INFO':
        logger.setLevel(logging.INFO)
    elif log_level.upper() == 'DEBUG':
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.ERROR)

    file_handler = logging.handlers.RotatingFileHandler(
        filename=log_file, maxBytes=1024*1024*20, backupCount=5)
    file_handler.setLevel(logging.DEBUG)

    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(threadName)s - %(levelname)s - %(message)s')

    file_handler.setFormatter(formatter)

    logger.addHandler(file_handler)


def backup_policy(logger):
    """
    实现一个完整的备份策略
    """

    # 自动发现当前主机上的实例
    logger.info(
        f"mmps use user='mysqldump' passwrod='{cnf.init_pwd}' to find instnaces")
    mmps = monitor.Mmps(monitor_user="mysqldump",
                        monitor_password=cnf.init_pwd)

    # 这样 ports 里就记录了当前主机上所有的实例
    ports = mmps.get_all_sql_port()
    logger.info(f"instances in current host {ports}")

    # 1、全量备份
    # 检查实例是否用全量备份，如果没有就给它来一个全量备份
    for port in ports:

        # 检查当前实例的可用备份工具
        tools = backup.usable_backup_tools(
            user="mysqldump", password=cnf.init_pwd, port=port, host="127.0.0.1")
        if tools is None or len(tools) == 0:
            logger.warning(
                f"there are not any available backup tools dbm-backup-proxy exits for mysqld-{port}")
            continue

        first, *_ = tools
        bc = backup.BackupChecker(port=port)
        if first == "mysqldump":
            if not bc.has_sql_backup:
                dump = backup.MySQLDumpFullBackup(
                    user="mysqldump", password=cnf.init_pwd, port=port)
                dump.backup()
            else:
                logger.info(f"has backup,skip backup for {port}")
        elif first == "mysqlbackup":
            if not bc.has_mbi_full_backup:
                dump = backup.MySQLBackupFullBackup(
                    user="mysqlbackup", password=cnf.init_pwd, port=port)
                dump.backup()
            else:
                logger.info(f"has backup,skip backup for {port}")

    # 2、差异备份
    for port in ports:
        # 检查当前实例的可用备份工具
        tools = backup.usable_backup_tools(
            user="mysqldump", password=cnf.init_pwd, port=port, host="127.0.0.1")
        if tools is None or len(tools) == 0:
            logger.warning(
                f"there are not any available backup tools dbm-backup-proxy exits for mysqld-{port}")
            continue

        first, *_ = tools
        bc = backup.BackupChecker(port=port)

        if first == 'mysqldump':
            if not bc.has_sql_backup:
                dump = backup.MySQLDumpFullBackup(
                    user="mysqldump", password=cnf.init_pwd, port=port)
                dump.backup()
            else:
                logger.info(f"has backup,skip backup for {port}")
        elif first == 'mysqlbackup':
            if not bc.has_mbi_diff_backup:
                dump = backup.MySQLBackupDiffBackup(
                    user="mysqlbackup", password=cnf.init_pwd, port=port)
                dump.backup()
            else:
                logger.info(f"has backup,skip backup for {port}")


def main():
    """
    # 监控网关的主程序
    """
    args = parser_cmd_args()

    # 程序要求以 root 身份运行
    if is_root() == False:

        print(f"must use root user to execute this program.", file=sys.stderr)
        sys.exit(1)

    # dbma 用户是否存在
    if not checkings.is_user_exists('dbma'):
        print("user 'dbma' not exists,use this fix it 'dbm-agent init' ")
        sys.exit(2)

    # 切换到普通用户 dbma # 切到 dbma 会引发一个 linux 上的一个 bug 导致备份不成功
    uid, gid = get_uid_gid('dbma')
    os.setegid(gid)
    os.seteuid(uid)

    # 查询用于保存日志文件的目录是否存在
    dir_name = os.path.dirname(args.log_file)
    if not checkings.is_directory_exists(dir_name):

        # 如果目录不存在就退出程序
        print(f"diectory '{dir_name}' not exists ")
        sys.exit(3)

    # 切到守护进程模式
    if args.action == 'stop':
        stop_daemon('/tmp/dbm-backup-proxy.pid')
    elif args.action == 'start':
        start_daemon('/tmp/dbm-backup-proxy.pid')
    else:
        print("not suported action")
        exit(4)

    # 程序运行到这里说明可以配置日志了
    config_log(log_file=args.log_file, log_level=args.log)

    # 拿到日志对象
    logger = logging.getLogger('dbm-agent')
    logger.info("dbm-backup-proxy start")
    print(f"Successful start and log file save to '{args.log_file}' ")

    # 在这里死循环内实现所有的功能
    while True:
        with common.sudo("execute backup policy"):
            try:
                logger.warning(f"euid = {os.geteuid()}")
                backup_policy(logger)
                logger.warning(f"euid = {os.geteuid()}")
            except Exception as err:
                logger.error(str(err))

        time.sleep(args.spin_time)


if __name__ == "__main__":
    main()
