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.JdbcExportComponent;
import com.njcn.gather.systemops.database.component.OracleJdbcComponent;
import com.njcn.gather.systemops.database.config.DbmsProperties;
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.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.sql.Connection;
import java.time.LocalDateTime;
import java.util.Locale;

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

    private final JdbcExportComponent jdbcExportComponent;
    private final OracleJdbcComponent oracleJdbcComponent;
    private final DbmsProperties dbmsProperties;

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

    @Override
    public DatabaseBackupFile executeBackup(DatabaseOperationTask task, DatabaseConnection connection, String password,
                                            DatabaseBackupParam.CreateParam param) throws Exception {
        String baseName = buildBaseFileName(connection, task);
        String fileName = DatabaseFileNameUtil.appendTodayWithTask(baseName + ".csv", task.getTaskNo());
        String metadataFileName = DatabaseFileNameUtil.appendTodayWithTask(baseName + "_metadata.json", task.getTaskNo());
        Path dataFilePath = buildManagedPath(dbmsProperties.getBackup().getStoragePath(), fileName);
        Path metadataFilePath = buildManagedPath(dbmsProperties.getBackup().getStoragePath(), metadataFileName);
        try (Connection jdbcConnection = oracleJdbcComponent.openConnection(connection, password)) {
            jdbcExportComponent.exportCsv(jdbcConnection, connection.getSchemaName(), param, dataFilePath, metadataFilePath);
        }
        return buildBackupFile(task, connection, param, fileName, dataFilePath, metadataFilePath);
    }

    private DatabaseBackupFile buildBackupFile(DatabaseOperationTask task, DatabaseConnection connection,
                                               DatabaseBackupParam.CreateParam param, String fileName, Path filePath,
                                               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(FileFormatEnum.CSV.name());
        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(null);
        file.setDumpFileName(null);
        file.setLogFileName(null);
        file.setFileName(fileName);
        file.setFilePath(filePath.toString());
        file.setLogFilePath(null);
        file.setMetadataFilePath(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);
    }
}
