package com.njcn.relational.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.njcn.relational.mapper.DynamicSyncMapper;
import com.njcn.relational.mapper.SyncTableConfigMapper;
import com.njcn.relational.pojo.bo.UploadResult;
import com.njcn.relational.pojo.po.SyncTableConfig;
import com.njcn.relational.pojo.po.SyncTableLogs;
import com.njcn.relational.serializer.BlobSerializer;
import com.njcn.relational.serializer.ClobSerializer;
import com.njcn.relational.utils.SftpUploadUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.io.*;
import java.sql.Blob;
import java.sql.Clob;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service
public class SyncTwoDmExportService extends ServiceImpl<SyncTableConfigMapper, SyncTableConfig> {

    @Autowired
    private SyncTableLogsService syncTableLogsService;

    @Autowired
    private DynamicSyncMapper dynamicSyncMapper;

    @Value("${sync.export.remotePath}")
    private String remotePath;

    @Value("${sync.export.localPath}")
    private String localPath;

    @Value("${sync.ip}")
    private String ip;

    @Value("${sync.port:22}")
    private Integer port;

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

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

    // 新增：表名 -> 时间字段 缓存
    private Map<String, String> timeColumnCache = new HashMap<>();
    private Map<String, String> dataTimeTypeCache = new HashMap<>();


    private static final int BATCH_SIZE = 10000;
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter FILE_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    // JSON序列化工具（自动处理时间、换行符等）
    private final ObjectMapper objectMapper = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .registerModule(new SimpleModule() {{
                addSerializer(Blob.class, new BlobSerializer());
                addSerializer(Clob.class, new ClobSerializer());
            }})
            .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"))
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .enable(SerializationFeature.INDENT_OUTPUT)
            .setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    ;  // 格式化输出，可读性好

    // 缓存当前Schema
    private String currentSchema;

    // 记录本次生成的所有文件
    private final List<String> generatedFiles = new ArrayList<>();

    @PostConstruct
    public void init() {
        currentSchema = dynamicSyncMapper.getCurrentUser();
        log.info("当前数据库Schema: {}", currentSchema);
    }

    /**
     * 获取所有启用的全量表（从配置表）
     */
    public List<SyncTableConfig> getEnabledFullTables() {
        LambdaQueryWrapper<SyncTableConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SyncTableConfig::getEnabled, 1)
                .eq(SyncTableConfig::getSyncMode, "FULL")
                .orderByAsc(SyncTableConfig::getSortOrder);
        return this.list(wrapper);
    }

    /**
     * 获取所有启用的增量表（从配置表）
     */
    public List<SyncTableConfig> getEnabledIncrementTables() {
        LambdaQueryWrapper<SyncTableConfig> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SyncTableConfig::getEnabled, 1)
                .eq(SyncTableConfig::getSyncMode, "INC")
                .orderByAsc(SyncTableConfig::getSortOrder);
        return this.list(wrapper);
    }

    /**
     * 确保本地目录存在
     */
    private void ensureLocalDirectory() {
        File dir = new File(localPath);
        if (!dir.exists()) {
            dir.mkdirs();
            log.info("创建目录: {}", localPath);
        }
    }


    /**
     * 全量同步所有表
     */
    public void syncAllFullTables(String dateStr) {
        List<SyncTableConfig> tables = getEnabledFullTables();
        log.info("========== 全量同步开始，共 {} 张表 ==========", tables.size());

        // 确保目录存在
        ensureLocalDirectory();
        generatedFiles.clear();

        for (SyncTableConfig config : tables) {
            String tableName = config.getTableName();
            try {
                long startTime = System.currentTimeMillis();

                /*IService<?> service = registry.getServiceByTableName(tableName);
                if (service != null) {
                    String filePath = exportFullTableWithEntity(service, tableName, dateStr);
                    log.info("表 {} 使用实体方式导出完成", tableName);
                } else {*/
                String filePath = exportFullTableToJson(tableName, dateStr);
                log.info("表 {} 使用Map方式导出完成", tableName);
                // }

                long duration = System.currentTimeMillis() - startTime;
                log.info("表 {} 全量导出完成，耗时 {} ms", tableName, duration);
            } catch (Exception e) {
                log.error("表 {} 全量导出失败", tableName, e);
            }
        }
        syncAllIncrementTables(dateStr);
    }

    /**
     * 增量同步所有表
     */
    public void syncAllIncrementTables(String dateStr) {
        List<SyncTableConfig> tables = getEnabledIncrementTables();
        timeColumnCache = tables.stream().filter(it -> StrUtil.isNotBlank(it.getTimeColumn())).collect(Collectors.toMap(SyncTableConfig::getTableName, SyncTableConfig::getTimeColumn));
        dataTimeTypeCache = tables.stream().filter(it -> StrUtil.isNotBlank(it.getDataTimeType())).collect(Collectors.toMap(SyncTableConfig::getTableName, SyncTableConfig::getDataTimeType));


        log.info("========== 增量同步开始，共 {} 张表 ==========", tables.size());
        boolean localFlag = true;
        boolean resultFlag = true;
        for (SyncTableConfig config : tables) {
            String tableName = config.getTableName();
            try {
                long startTime = System.currentTimeMillis();

                String filePath = exportTodayDataToJson(tableName, dateStr);
                log.info("表 {} 使用Map方式导出完成", tableName);

                long duration = System.currentTimeMillis() - startTime;
                log.info("表 {} 增量导出完成，耗时 {} ms", tableName, duration);
            } catch (Exception e) {
                log.error("表 {} 增量同步失败", tableName, e);
                localFlag = false;
            }
        }

        // 推送所有文件到横向隔离设备
        if (!generatedFiles.isEmpty()) {
            try {
                uploadAllFiles();
            } catch (Exception e) {
                log.error("批量上传失败", e);
                resultFlag = false;
            }
        } else {
            resultFlag = false;
        }

        SyncTableLogs syncTableLogs = new SyncTableLogs();
        syncTableLogs.setLocalResult(localFlag?1:0);
        syncTableLogs.setResult((resultFlag&&localFlag) ? 1:0);
        syncTableLogs.setTimeId(LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyyMMdd")));
        syncTableLogs.setCreateTime(LocalDateTime.now());
        syncTableLogsService.saveOrUpdate(syncTableLogs);

    }

    /**
     * 上传所有生成的文件（批量上传）
     */
    private void uploadAllFiles() throws Exception {
        if (generatedFiles.isEmpty()) {
            log.info("没有文件需要上传");
            return;
        }


        List<UploadResult> resultList = SftpUploadUtil.batchUploadFiles(
                ip, port, username, password, generatedFiles, remotePath
        );

        // 统计上传结果
        long successCount = resultList.stream().filter(UploadResult::isSuccess).count();
        long failCount = resultList.size() - successCount;

        log.info("批量上传完成 - 成功: {}, 失败: {}, 总计: {}", successCount, failCount, resultList.size());
        // 记录失败的文件
        for (UploadResult result : resultList) {
            if (!result.isSuccess()) {
                log.error("上传失败文件: {}, 原因: {}", result.getLocalFilePath(), result.getErrorMessage());
            }
        }

    }

    /**
     * Map方式全量导出为JSON（保存为.txt文件）
     */
    @Transactional
    public String exportFullTableToJson(String tableName, String dateStr) throws IOException {
        String fileName = String.format("%s_FULL_%s.txt", tableName, dateStr);
        String filePath = localPath + fileName;

        log.info("开始导出全量表: {} -> {}", tableName, filePath);

        List<Map<String, Object>> allData = new ArrayList<>();
        int offset = 0;

        while (true) {
            List<Map<String, Object>> batchData = dynamicSyncMapper.selectPage(
                    tableName, BATCH_SIZE, offset);

            if (batchData.isEmpty()) {
                break;
            }

            allData.addAll(batchData);
            log.info("表 {} 已读取 {} 条记录", tableName, allData.size());

            if (batchData.size() < BATCH_SIZE) {
                break;
            }
            offset += BATCH_SIZE;
        }

        // 写入JSON格式到.txt文件
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
            objectMapper.writeValue(bos, allData);
        }


        // 记录生成的文件
        generatedFiles.add(filePath);

        log.info("表 {} 全量导出完成，共 {} 条记录，文件: {}", tableName, allData.size(), filePath);
        return filePath;
    }

    /**
     * Map方式增量导出为JSON（保存为.txt文件）
     */
    @Transactional
    public String exportTodayDataToJson(String tableName, String dateStr) throws IOException {

        // 1. 判断表属于哪种周期：D/M/Q/Y
        String periodType = getTablePeriodType(tableName);
        if (periodType == null) {
            log.error("表 {} 无法识别周期类型，跳过增量导出", tableName);
            return null;
        }

        // 2. 根据周期构建 开始/结束 时间
        Map<String, String> timeRange = buildPeriodTimeRange(periodType, dateStr);
        String dateStart = timeRange.get("start");
        String dateEnd = timeRange.get("end");

        log.info("导出表 {} 数据: {} 至 {}", tableName, dateStart, dateEnd);

        String timeColumn = getTimeColumn(tableName);

        List<Map<String, Object>> queryData = dynamicSyncMapper.selectTodayData(
                tableName, timeColumn, dateStart, dateEnd);

        if (queryData.isEmpty()) {
            log.info("表 {} {} - {}无数据", tableName, dateStart, dateEnd);
            return null;
        }

        String fileName = String.format("%s_INC_%s.txt", tableName, dateStr);
        String filePath = localPath + fileName;

        // 写入JSON格式到.txt文件
        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
            objectMapper.writeValue(bos, queryData);
        }

        // 记录生成的文件
        generatedFiles.add(filePath);

        log.info("表 {} {}数据导出完成，共 {} 条记录，文件: {}", tableName, dateStr, queryData.size(), filePath);
        return filePath;

    }


    /**
     * 从表名识别周期类型
     * D=天, M=月, Q=季, Y=年
     */
    private String getTablePeriodType(String tableName) {
        String dTypeCreateTitle = dataTimeTypeCache.get(tableName);
        if (dTypeCreateTitle != null) {
            if ("D".equals(dTypeCreateTitle) || "M".equalsIgnoreCase(tableName) || "Q".equals(tableName) || "Y".equals(tableName)) {
                return dTypeCreateTitle;
            }
        } else {
            if (tableName.endsWith("_D")) {
                return "D";
            }
            if (tableName.endsWith("_M")) {
                return "M";
            }
            if (tableName.endsWith("_Q")) {
                return "Q";
            }
            if (tableName.endsWith("_Y")) {
                return "Y";
            }
        }
        return null;
    }

    /**
     * 根据周期 & 入参日期，计算【当前周期】时间范围（JDK8 兼容）
     */
    private Map<String, String> buildPeriodTimeRange(String periodType, String dateStr) {
        DateTime baseDate = DateUtil.parse(dateStr, DatePattern.PURE_DATE_PATTERN);
        String start;
        String end;

        // JDK8 标准 switch
        switch (periodType) {
            case "D":
                // 入参日期 当天
                start = DateUtil.format(DateUtil.beginOfDay(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                end = DateUtil.format(DateUtil.endOfDay(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                break;
            case "M":
                // 入参日期 所在整月
                start = DateUtil.format(DateUtil.beginOfMonth(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                end = DateUtil.format(DateUtil.endOfMonth(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                break;
            case "Q":
                // 入参日期 所在整季度
                start = DateUtil.format(DateUtil.beginOfQuarter(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                end = DateUtil.format(DateUtil.endOfQuarter(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                break;
            case "Y":
                // 入参日期 所在整年
                start = DateUtil.format(DateUtil.beginOfYear(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                end = DateUtil.format(DateUtil.endOfYear(baseDate), DatePattern.NORM_DATETIME_PATTERN);
                break;
            default:
                throw new IllegalArgumentException("不支持的周期类型：" + periodType);
        }

        Map<String, String> map = new HashMap<>(2);
        map.put("start", start);
        map.put("end", end);
        return map;
    }


    /**
     * 获取时间字段
     */
    private String getTimeColumn(String tableName) {
        String createTitle = timeColumnCache.get(tableName);
        if (Objects.isNull(createTitle)) {
            switch (tableName) {
                case "R_STAT_DATA_FLICKER_D":
                case "R_STAT_DATA_FLUC_D":
                case "R_STAT_DATA_HARMPHASIC_I_D":
                case "R_STAT_DATA_HARMPHASIC_V_D":
                case "R_STAT_DATA_HARMPOWER_P_D":
                case "R_STAT_DATA_HARMPOWER_Q_D":
                case "R_STAT_DATA_HARMPOWER_S_D":
                case "R_STAT_DATA_HARMRATE_I_D":
                case "R_STAT_DATA_HARMRATE_V_D":
                case "R_STAT_DATA_INHARM_I_D":
                case "R_STAT_DATA_INHARM_V_D":
                case "R_STAT_DATA_I_D":
                case "R_STAT_DATA_PLT_D":
                case "R_STAT_DATA_V_D":
                    return "TIME";
                // 新增表
                case "R_STAT_COMASSES_D":
                case "R_STAT_ASSES_D":
                case "R_STAT_LIMIT_RATE_D":
                case "R_STAT_ONLINERATE_D":
                case "R_STAT_INTEGRITY_D":
                case "R_STAT_ORG_INTEGRITY_D":
                case "R_STAT_LIMIT_RATE_DETAIL_D":
                case "R_STAT_LIMIT_TARGET_D":
                    return "TIME_ID";
                case "R_MP_POLLUTION_D":
                case "R_MP_V_THD":
                case "R_STAT_POLLUTION_ORG_D":
                case "R_STAT_POLLUTION_ORG_M":
                case "R_STAT_POLLUTION_ORG_Q":
                case "R_STAT_POLLUTION_ORG_Y":
                case "R_STAT_POLLUTION_SUBSTATION_D":
                case "R_STAT_POLLUTION_SUBSTATION_M":
                case "R_STAT_POLLUTION_SUBSTATION_Q":
                case "R_STAT_POLLUTION_SUBSTATION_Y":
                    return "DATA_DATE";
                default:
                    log.warn("表 {} 未配置时间字段，跳过", tableName);
                    return "CREATE_TIME";
            }
        } else {
            return createTitle;
        }
    }

    public void test() {
        try {
            // 确保目录存在
            File localDir = new File(localPath);
            if (!localDir.exists()) {
                localDir.mkdirs();
            }

            // 生成txt文件
            String filePath = localPath + "test.txt";
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
                writer.write("test");
            }

            // 上传到SFTP服务器
            SftpUploadUtil.uploadFile(ip, port, username, password, filePath, remotePath);

            log.info("test.txt文件生成并上传成功");

        } catch (Exception e) {
            log.error("失败", e);
        }
    }

    public List<String> getPendingFiles() {
        List<String> matchedFiles = new ArrayList<>();
        Vector<?> files;
        try {
            // 列目录也纳入异常捕获
            files = SftpUploadUtil.listFiles(ip, port, username, password, remotePath);
            for (Object obj : files) {
                if (obj instanceof com.jcraft.jsch.ChannelSftp.LsEntry) {
                    com.jcraft.jsch.ChannelSftp.LsEntry entry = (com.jcraft.jsch.ChannelSftp.LsEntry) obj;
                    String fileName = entry.getFilename();
                    matchedFiles.add(fileName);
                }
            }
        } catch (Exception e) {
            log.error("获取待处理文件列表失败，远端目录:{}", remotePath, e);
            return new ArrayList<>();
        }
        return matchedFiles;
    }

    /**
     * 清理本地已处理过的文件
     */
    public void cleanLocalFiles(int daysToKeep) {
        File localDir = new File(localPath);
        if (!localDir.exists()) {
            return;
        }

        long cutoffTime = System.currentTimeMillis() - (daysToKeep * 24L * 60 * 60 * 1000);
        File[] files = localDir.listFiles();

        if (files != null) {
            int deletedCount = 0;
            for (File file : files) {
                if (file.isFile() && file.lastModified() < cutoffTime) {
                    file.delete();
                    deletedCount++;
                }
            }
            log.info("清理了10.11.7.5二区服务器 {} 个七天前的本地txt文件", deletedCount);
        }
    }
}
