package com.casic.missiles.job.handler;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import static com.xxl.job.core.biz.model.ReturnT.SUCCESS;

/**
 * @Description: 数据库备份任务
 *              注：需在项目启动服务器中安装MySQL使用mysqldump命令，或和数据库部署在同一服务器
 * @Author: wangpeng
 * @Date: 2023/7/4 13:39
 */
@Slf4j
@Component
public class MysqlDataBackupHandler {
    /**
     * 备份 sql 存放目录(相对路径, 注意可能需要在 MvcConfig 配置访问权限)
     */
    String filePath = "/backupSql/";

    /**
     * 数据库版本是否为 8.0 + （false=否  true=是）， mysql8+ 需要参数  --column-statistics=0  ， mysql8- 不需要
     */
    Boolean isDbVersion8 = true;

    /**
     * 备份命令
     * USERNAME   账号
     * PASSWORD   密码
     * SERVERPATH 服务器IP/域名
     * DBNAME     数据库名称
     * FILEPATH   备份文件存放地址+名称
     * 说明
     * cmdCompression ： 需压缩 （本地或服务器需安装 mysqldump 命令(安装mysql自带患独立安装) +  gzip 命令(独立安装)）
     * cmd ：            不压缩 (本地或服务器需安装 mysqldump 命令(安装mysql自带患独立安装)
     * --column-statistics=0     mysql8 添加该参数, 非mysql8 不添加, 否则将出错
     */
    String cmdMysql8 = "mysqldump --column-statistics=0  -u{USERNAME} -p{PASSWORD} -h{SERVER} -P{PORT} --databases {DBNAME} > {FILEPATH}";
    String cmd = "mysqldump -u{USERNAME} -p{PASSWORD} -h{SERVER} -P{PORT} --databases {DBNAME} > {FILEPATH}";


    @Value("${spring.datasource.url}")
    private String dbUrl;

    @Value("${spring.datasource.username}")
    private String dbUserName;

    @Value("${spring.datasource.password}")
    private String dbPassWord;


    // TODO: 2023/7/4 数据库备份Linux测试，备份文件恢复测试
    /**
     * 每天凌晨4点执行
     */
    @XxlJob(value = "mysqlDataBackupHandler")
    public ReturnT<String> execute() {
        XxlJobHelper.log("【备份数据库】--START");
        log.info("【备份数据库】--START");
        String path = System.getProperty("user.dir");
        String finalPath = path + filePath;
        log.info("【备份数据库】,文件存储路径为：{}", finalPath);
        String dbUrl2 = dbUrl.replace("jdbc:mysql://", "");
        // 获取数据库名称
        String dbName = dbUrl2.substring(dbUrl2.indexOf("/") + 1, dbUrl2.indexOf("?"));
        // 获取数据库地址
        String serverPath = dbUrl2.substring(0, dbUrl2.indexOf("/"));
        String server = serverPath.substring(0, serverPath.indexOf(":"));
        String port = serverPath.substring(serverPath.indexOf(":") + 1);
        // 数据库账号
        String username = dbUserName;
        // 数据库密码
        String password = dbPassWord;

        // 备份文件目录+名称  备份文件存放目录+名称(名称 = 数据库名+时间字符串.sql)
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime localDateTime = LocalDateTime.now();
        String timeStr = formatter.format(localDateTime);
        String pathFileName = finalPath + dbName + "_" + timeStr + ".sql";
        String newCmd = "";
        if (isDbVersion8) {
            newCmd = cmdMysql8;
        } else {
            newCmd = cmd;
        }
        // 执行命令
        newCmd =  newCmd.replace("{USERNAME}", username)
                .replace("{PASSWORD}", password)
                .replace("{SERVER}", server)
                .replace("{PORT}", port)
                .replace("{DBNAME}", dbName)
                .replace("{FILEPATH}", pathFileName);
        System.out.println(newCmd);
        PrintWriter printWriter = null;
        BufferedReader bufferedReader = null;
        try {
            // 创建存放sql的文件
            existsFile(new File(pathFileName));
            printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(pathFileName), "utf8"));
            Process process = null;
            String property = System.getProperty("os.name");
            System.out.println(property);
            if (property.indexOf("Linux") != -1) {
                // linux
                process = Runtime.getRuntime().exec(new String[]{"bash", "-c", newCmd});
            } else {
                // 本地win
                process = Runtime.getRuntime().exec(newCmd);
            }
            InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream(), "utf8");
            bufferedReader = new BufferedReader(inputStreamReader);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                printWriter.println(line);
            }
            // 此次会执行过长时间,直到备份完成
            printWriter.flush();
            printWriter.close();
            //0 表示线程正常终止。
//            if (process.waitFor() == 0) {
//                // 线程正常执行
//                log.info("【备份数据库】SUCCESS，SQL文件：{}", pathFileName);
//            }
            process.waitFor();
            log.info("【备份数据库】SUCCESS，SQL文件：{}", pathFileName);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("【备份数据库】FAILURE");
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if (printWriter != null) {
                    printWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.info("【备份数据库】--END");
        return SUCCESS;
    }

    /**
     * 定期删除备份，只保留最近一个星期
     * 每天凌晨5点执行
     */
    @XxlJob(value = "mysqlDataBackupDeleteHandler")
    public ReturnT<String> deleteBackUpDataBase() {
        log.info("【删除备份数据库】--START");
        String path = System.getProperty("user.dir");
        String finalPath = path + filePath;
        File backUpFile = new File(finalPath);
        if (backUpFile.exists()) {
            File[] files = backUpFile.listFiles();
            for (File file : files) {
                if (!file.isDirectory()) {
                    String substring = file.getName().substring(file.getName().indexOf("2"), file.getName().indexOf("."));
                    Date date1 = DateUtil.parse(substring, "yyyy-MM-dd");
                    Date date2 = new Date();
                    long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);
                    if (betweenDay > 7) {
                        file.delete();
                        log.info("【删除备份数据库】SUCCESS，SQL文件：{}", file.getName());
                    }
                }
            }
        }
        log.info("【删除备份数据库】--END");
        return SUCCESS;
    }

    /**
     * 判断文件是否存在，不存在创建
     */
    private static void existsFile(File file) {
        // 判断文件路径是否存在,不存在新建
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
