package com.qmth.distributed.print.business.backup;

import cn.hutool.core.date.DateUtil;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.teachcloud.common.config.DictionaryConfig;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.SysConfig;
import com.qmth.teachcloud.common.entity.SysOrg;
import com.qmth.teachcloud.common.entity.SysRole;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.LocalCatalogEnum;
import com.qmth.teachcloud.common.enums.UploadFileEnum;
import com.qmth.teachcloud.common.service.*;
import com.qmth.teachcloud.common.util.FileStoreUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @Description: MySQL数据库备份
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2022/11/14
 */
@Component
public class MySQLDatabaseBackup {
    private final static Logger log = LoggerFactory.getLogger(MySQLDatabaseBackup.class);

    @Value("${db.host}")
    String host;

    @Value("${db.port}")
    String port;

    @Value("${db.name}")
    String databaseName;

    @Value("${db.username}")
    String username;

    @Value("${db.password}")
    String password;

    @Resource
    SysRoleService sysRoleService;

    @Resource
    SysUserService sysUserService;

    @Resource
    CommonCacheService commonCacheService;

    @Resource
    SysOrgService sysOrgService;

    @Resource
    SysConfigService sysConfigService;

    @Resource
    FileStoreUtil fileStoreUtil;

    @Resource
    DictionaryConfig dictionaryConfig;

    /**
     * Java代码实现MySQL数据库导出
     *
     * @return
     * @throws Exception
     */
    public boolean exportDatabaseTool() throws Exception {
        File file = SystemConstant.getFileTempVar(".sql");
        PrintWriter printWriter = null;
        BufferedReader bufferedReader = null;
        try {
            printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8));
            String execSql = "mysqldump -h" + this.host + " -P" + this.port + " -u" + this.username + " -p" + this.password + " --set-charset=" + StandardCharsets.UTF_8 + " " + this.databaseName;
            Process process = Runtime.getRuntime().exec(execSql);
            InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8);
            bufferedReader = new BufferedReader(inputStreamReader);
            IOUtils.copy(bufferedReader, printWriter);
            if (process.waitFor() == 0) {//0 表示线程正常终止。
                boolean oss = dictionaryConfig.sysDomain().isOss();
                StringJoiner stringJoiner = new StringJoiner("");
                if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                    stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
                }
                stringJoiner = SystemConstant.getDirName(UploadFileEnum.FILE, true);
                stringJoiner.add("db").add(File.separator).add("backup").add(File.separator)
                        .add(DateUtil.format(new Date(), SystemConstant.BACK_UP_DATE_PATTERN)).add("_").add(NanoIdUtils.randomNanoId())
                        .add("_backup.sql");
                if (oss || (!oss && dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {
                    fileStoreUtil.ossUpload(stringJoiner.toString(), file, DigestUtils.md5Hex(new FileInputStream(file)), UploadFileEnum.FILE.getFssType());
                } else {
                    fileStoreUtil.localUpload(stringJoiner.toString(), new FileInputStream(file), DigestUtils.md5Hex(new FileInputStream(file)), LocalCatalogEnum.LOCAL_FILE);
                }
                return true;
            }
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (Objects.nonNull(bufferedReader)) {
                bufferedReader.close();
            }
            if (Objects.nonNull(printWriter)) {
                printWriter.close();
            }
            if (Objects.nonNull(file)) {
                file.delete();
            }
        }
        return false;
    }

    /**
     * 删除数据库数据
     *
     * @param fileName
     * @param schoolId
     * @throws Exception
     */
    public void deleteDbTool(String fileName, Long schoolId) throws Exception {
        Optional.ofNullable(fileName).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("文件名不能为空"));
        Optional.ofNullable(schoolId).orElseThrow(() -> ExceptionResultEnum.ERROR.exception("学校id不能为空"));

        InputStream inputStream = MySQLDatabaseBackup.class.getClassLoader().getResourceAsStream(fileName);
        Optional.ofNullable(inputStream).orElseThrow(() -> ExceptionResultEnum.ERROR.exception(fileName + "未找到"));
        File file = SystemConstant.getFileTempVar(".sql");
        FileUtils.copyInputStreamToFile(inputStream, file);

        ByteArrayOutputStream ou = new ByteArrayOutputStream();
        IOUtils.copy(new FileInputStream(file), ou);
        String string = new String(ou.toByteArray(), StandardCharsets.UTF_8);
        if (Objects.nonNull(string)) {
            string = string.replaceAll("\\#\\{schoolId\\}", String.valueOf(schoolId));
        }
        IOUtils.write(string.getBytes(StandardCharsets.UTF_8), new FileOutputStream(file));

        //删除部分重要缓存
        //角色
        QueryWrapper<SysRole> sysRoleQueryWrapper = new QueryWrapper<>();
        sysRoleQueryWrapper.lambda().eq(SysRole::getSchoolId, schoolId);
        List<SysRole> sysRoleList = sysRoleService.list(sysRoleQueryWrapper);

        //用户
        QueryWrapper<SysUser> sysUserQueryWrapper = new QueryWrapper<>();
        sysUserQueryWrapper.lambda().eq(SysUser::getSchoolId, schoolId);
        List<SysUser> sysUserList = sysUserService.list(sysUserQueryWrapper);

        //机构
        QueryWrapper<SysOrg> sysOrgQueryWrapper = new QueryWrapper<>();
        sysOrgQueryWrapper.lambda().eq(SysOrg::getSchoolId, schoolId);
        List<SysOrg> sysOrgList = sysOrgService.list(sysOrgQueryWrapper);

        //配置项
        QueryWrapper<SysConfig> sysConfigQueryWrapper = new QueryWrapper<>();
        sysConfigQueryWrapper.lambda().eq(SysConfig::getSchoolId, schoolId);
        List<SysConfig> sysConfigList = sysConfigService.list(sysConfigQueryWrapper);

        String cmdarray[] = {"mysql -h" + this.host + " -P" + this.port + " -u" + this.username + " -p" + this.password + " " + this.databaseName, "source " + file.getAbsolutePath()};
        Runtime runtime = Runtime.getRuntime();
        Process process = null;
        try {
            process = runtime.exec(cmdarray[0]);//cmd之后执行数组的第一个条件进入数据库
            //执行了第一条命令以后已经登录到mysql了
            Process finalProcess = process;
            new Thread(() -> {
                OutputStream os = finalProcess.getOutputStream();
                OutputStreamWriter writer = new OutputStreamWriter(os);
                try {
                    IOUtils.write(cmdarray[1], writer);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (Objects.nonNull(writer)) {
                            writer.close();
                        }
                        if (Objects.nonNull(os)) {
                            os.close();
                        }
                    } catch (Exception e) {
                        log.error(SystemConstant.LOG_ERROR, e);
                    }
                }
            }).start();
        } catch (Exception e) {
            log.error(SystemConstant.LOG_ERROR, e);
        } finally {
            if (process.waitFor() == 0 && Objects.nonNull(file)) {
                string = DateUtil.format(new Date(), SystemConstant.DEFAULT_DATE_PATTERN) + "删除数据成功！！！";
                IOUtils.write(string.getBytes(StandardCharsets.UTF_8), new FileOutputStream(file));

                boolean oss = dictionaryConfig.sysDomain().isOss();
                StringJoiner stringJoiner = new StringJoiner("");
                if (!oss && Objects.nonNull(dictionaryConfig.fssPublicDomain()) && !StringUtils.isBlank(dictionaryConfig.fssPublicDomain().getConfig()) && !dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT)) {
                    stringJoiner.add(dictionaryConfig.fssPublicDomain().getConfig()).add(File.separator);
                }
                stringJoiner = SystemConstant.getDirName(stringJoiner, UploadFileEnum.FILE, true);
                stringJoiner.add("db").add(File.separator).add("delete").add(File.separator)
                        .add(DateUtil.format(new Date(), SystemConstant.BACK_UP_DATE_PATTERN)).add("_").add(NanoIdUtils.randomNanoId())
                        .add("_").add(String.valueOf(schoolId)).add("_delete.sql");
                if (oss || (!oss && dictionaryConfig.fssPublicDomain().getConfig().startsWith(SystemConstant.START_PARENT))) {
                    fileStoreUtil.ossUpload(stringJoiner.toString(), file, DigestUtils.md5Hex(new FileInputStream(file)), UploadFileEnum.FILE.getFssType());
                } else {
                    fileStoreUtil.localUpload(stringJoiner.toString(), new FileInputStream(file), DigestUtils.md5Hex(new FileInputStream(file)), LocalCatalogEnum.LOCAL_FILE);
                }

                if (!CollectionUtils.isEmpty(sysRoleList)) {
                    for (SysRole s : sysRoleList) {
                        commonCacheService.removeRoleCache(s.getId());
//                        commonCacheService.removeRolePrivilegeCache(schoolId, s.getId());
                    }
                }

                if (!CollectionUtils.isEmpty(sysUserList)) {
                    for (SysUser s : sysUserList) {
                        SystemConstant.deleteUserCache(s.getId());
                    }
                }

                if (!CollectionUtils.isEmpty(sysOrgList)) {
                    for (SysOrg s : sysOrgList) {
                        commonCacheService.removeOrgCache(s.getId());
                    }
                }

                if (!CollectionUtils.isEmpty(sysConfigList)) {
                    for (SysConfig s : sysConfigList) {
                        commonCacheService.removeSysConfigCache(s.getSchoolId(), s.getConfigKey());
                    }
                }
                commonCacheService.removeSchoolRoleCache(schoolId);
                commonCacheService.removeSchoolPrivilegeCache(schoolId);
                if (Objects.nonNull(file)) {
                    file.delete();
                }
            }
        }
    }

//    public static void main(String[] args) {
//        MySQLDatabaseBackup mySQLDatabaseBackup = new MySQLDatabaseBackup();
//        try {
//            String host = "127.0.0.1", userName = "root", password = "123456789", databaseName = "distributed-v3.2.0";
//            Long schoolId = 290869043907264512L;
//            if (mySQLDatabaseBackup.exportDatabaseTool(this.host, userName, password, DateUtil.format(new Date(), SystemConstant.BACK_UP_DATE_PATTERN) + "_" + NanoIdUtils.randomNanoId() + "_backup.sql", databaseName)) {
//                System.out.println("数据库成功备份！！！");
////                deleteDbTool(host, userName, password, databaseName, SystemConstant.PRINT_DELETE_DATA_FILE_NAME, schoolId);
//                mySQLDatabaseBackup.deleteDbTool(host, userName, password, databaseName, "/Users/king/git/distributed-print-service/distributed-print-business/src/main/resources/db/delete-data.sql", schoolId);
//                System.out.println("删除数据成功！！！");
//            } else {
//                System.out.println("数据库备份失败！！！");
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
}