package com.njcn.gather.systemops.database.support.oracle;

import cn.hutool.core.util.StrUtil;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.systemops.database.component.DataPumpCommandExecutor;
import com.njcn.gather.systemops.database.pojo.enums.BackupModeEnum;
import com.njcn.gather.systemops.database.pojo.enums.BackupStrategyEnum;
import com.njcn.gather.systemops.database.pojo.enums.FileFormatEnum;
import com.njcn.gather.systemops.database.pojo.param.DatabaseBackupParam;
import com.njcn.gather.systemops.database.pojo.po.DatabaseBackupFile;
import com.njcn.gather.systemops.database.pojo.po.DatabaseConnection;
import com.njcn.gather.systemops.database.pojo.po.DatabaseOperationTask;
import com.njcn.gather.systemops.database.service.DatabaseBackupFileService;
import com.njcn.gather.systemops.database.support.spi.DatabaseBackupOperator;
import com.njcn.gather.systemops.database.util.DatabaseChecksumUtil;
import com.njcn.gather.systemops.database.util.DatabaseFileNameUtil;
import com.njcn.gather.systemops.database.util.DatabaseOpsIdUtil;
import com.njcn.gather.systemops.database.util.DatabasePathUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Locale;

/**
 * Oracle DATA_PUMP 备份实现。
 */
@Component
@RequiredArgsConstructor
public class OracleDataPumpBackupOperator implements DatabaseBackupOperator {

    private final DataPumpCommandExecutor dataPumpCommandExecutor;
    private final DatabaseBackupFileService databaseBackupFileService;

    @Override
    public boolean support(String dbType, String backupStrategy) {
        return "ORACLE".equalsIgnoreCase(dbType) && BackupStrategyEnum.DATA_PUMP.name().equals(backupStrategy);
    }

    @Override
    public DatabaseBackupFile executeBackup(DatabaseOperationTask task, DatabaseConnection connection, String password,
                                            DatabaseBackupParam.CreateParam param) {
        String directoryName = StrUtil.blankToDefault(param.getDirectoryName(), connection.getDirectoryName());
        if (StrUtil.isBlank(directoryName)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "DATA_PUMP 备份需要 Oracle Directory 名称");
        }
        String baseName = buildBaseFileName(connection, task);
        String dumpFileName = DatabaseFileNameUtil.appendTodayWithTask(baseName + ".dmp", task.getTaskNo());
        String logFileName = DatabaseFileNameUtil.appendTodayWithTask(baseName + ".log", task.getTaskNo());
        DataPumpCommandExecutor.CommandResult commandResult = dataPumpCommandExecutor.expdp(connection, password,
                directoryName, dumpFileName, logFileName, param.getTargetNames());
        if (!Boolean.TRUE.equals(commandResult.getSuccess())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "Data Pump 执行失败：" + commandResult.getOutput());
        }
        if (StrUtil.isBlank(connection.getDirectoryPath())) {
            throw new BusinessException(CommonResponseEnum.FAIL, "Data Pump 备份需要配置可管理的 directoryPath");
        }
        Path dumpPath = buildManagedPath(connection.getDirectoryPath(), dumpFileName);
        Path logPath = buildManagedPath(connection.getDirectoryPath(), logFileName);
        return buildBackupFile(task, connection, param, FileFormatEnum.DMP.name(), dumpFileName, dumpPath, logFileName, logPath, null);
    }

    private DatabaseBackupFile buildBackupFile(DatabaseOperationTask task, DatabaseConnection connection,
                                               DatabaseBackupParam.CreateParam param, String fileFormat, String fileName,
                                               Path filePath, String logFileName, Path logFilePath, Path metadataFilePath) {
        if (filePath == null || !Files.exists(filePath)) {
            throw new BusinessException(CommonResponseEnum.FAIL, "备份文件未生成");
        }
        DatabaseBackupFile file = new DatabaseBackupFile();
        file.setId(DatabaseOpsIdUtil.uuid());
        file.setTaskId(task.getId());
        file.setConnectionId(connection.getId());
        file.setDbType(connection.getDbType());
        file.setBackupStrategy(task.getBackupStrategy());
        file.setFileFormat(fileFormat);
        file.setSchemaName(task.getSchemaName());
        file.setTargetNamesJson(task.getTargetNamesJson());
        file.setBackupMode(StrUtil.blankToDefault(param.getBackupMode(), BackupModeEnum.FULL_TABLE.name()).toUpperCase(Locale.ROOT));
        file.setBackupStartTime(param.getStartTime());
        file.setBackupEndTime(param.getEndTime());
        file.setTimeColumn(param.getTimeColumn());
        file.setDirectoryName(StrUtil.blankToDefault(param.getDirectoryName(), connection.getDirectoryName()));
        file.setDumpFileName(FileFormatEnum.DMP.name().equals(fileFormat) ? fileName : null);
        file.setLogFileName(logFileName);
        file.setFileName(fileName);
        file.setFilePath(filePath.toString());
        file.setLogFilePath(logFilePath == null ? null : logFilePath.toString());
        file.setMetadataFilePath(metadataFilePath == null ? null : metadataFilePath.toString());
        file.setFileSize(readFileSize(filePath));
        file.setChecksum(DatabaseChecksumUtil.sha256(filePath));
        file.setState(1);
        file.setCreateTime(LocalDateTime.now());
        file.setUpdateTime(LocalDateTime.now());
        return file;
    }

    private Long readFileSize(Path filePath) {
        try {
            if (filePath != null && Files.exists(filePath) && !Files.isDirectory(filePath)) {
                return Files.size(filePath);
            }
        } catch (Exception ignored) {
            return null;
        }
        return null;
    }

    private Path buildManagedPath(String rootPath, String fileName) {
        Path root = DatabasePathUtil.normalize(rootPath);
        if (root == null) {
            throw new BusinessException(CommonResponseEnum.FAIL, "备份目录未配置");
        }
        return root.resolve(fileName).normalize();
    }

    private String buildBaseFileName(DatabaseConnection connection, DatabaseOperationTask task) {
        return connection.getSchemaName() + "_" + task.getBackupStrategy().toLowerCase(Locale.ROOT);
    }
}
