package com.awspaas.user.apps.bnbm.datalinkup.job;

import com.actionsoft.bpms.bo.engine.BO;
import com.actionsoft.bpms.commons.database.RowMap;
import com.actionsoft.bpms.schedule.IJob;
import com.actionsoft.bpms.server.UserContext;
import com.actionsoft.bpms.util.DBSql;
import com.actionsoft.sdk.local.SDK;
import com.actionsoft.sdk.local.api.cc.RDSAPI;
import com.awspaas.user.apps.bnbm.datalinkup.enums.Section;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @ClassName: WaterproofPaintAccountsReceivableJob
 * @Description: 防水涂料计算应收单 - 多线程版本
 * @date: 2025/8/29 16:38
 * @Blog: https://
 */
public class WaterproofPaintAccountsReceivableJob implements IJob {
    private static final Logger LOGGER = LoggerFactory.getLogger(WaterproofPaintAccountsReceivableJob.class);

    // 定义常量
    private static final String RDS_ID = "e19d738a-8eac-4c42-9c08-7fe93e75638d";
    private static final String DEFAULT_BKGS_FS = "北新防水";
    private static final String DEFAULT_BKGS_TL = "北新涂料";
    private static final int BATCH_SIZE = 1000;
    private static final int THREAD_POOL_SIZE = 2; // 线程池大小，根据需求调整
    // 板块配置

    @Override
    public void execute(JobExecutionContext job) throws JobExecutionException {
        try {
            String param = SDK.getJobAPI().getJobParameter(job);
            int timeRange = StringUtils.isNotBlank(param)?Integer.parseInt(param):0;
            // 获取当前时间
            LocalDate nowDate = LocalDate.now();
            LocalDate startDate;
            if (timeRange > 0) {
                startDate = nowDate.minusDays(timeRange);
            } else {
                // 默认计算去年第一天到现在的数据
                startDate = nowDate.minusYears(1).withDayOfYear(1);
            }
            LOGGER.info("开始执行防水涂料应收单计算任务，时间范围：{} 到 {}", startDate, nowDate);
            // 依次处理防水和涂料两个板块
            boolean allSuccess = true;
            for (Section section : Section.values()) {
                boolean success = processSection(section, startDate, nowDate);
                if (!success) {
                    allSuccess = false;
                    LOGGER.error("{}板块处理失败", section.getName());
                }
            }
            if (!allSuccess) {
                throw new JobExecutionException("部分板块处理失败");
            }
            LOGGER.info("防水涂料应收单计算任务完成，共处理 {} 天数据", nowDate.toEpochDay() - startDate.toEpochDay() + 1);

        } catch (Exception e) {
            LOGGER.error("执行防水涂料应收单计算任务时发生错误", e);
            throw new JobExecutionException("任务执行失败", e);
        }
    }
    /**
     * 处理单个板块的数据
     * @param section 板块配置
     * @param startDate 开始日期
     * @param nowDate 结束日期
     * @return 处理是否成功
     */
    private boolean processSection(Section section, LocalDate startDate, LocalDate nowDate) {
        String sectionName = section.getName();
        LOGGER.info("开始处理{}板块数据，时间范围：{} 到 {}", sectionName, startDate, nowDate);

        // 每个板块使用独立的缓存
        Map<String, Map<LocalDate, BigDecimal>> historicalBalanceCache = new HashMap<>();
        // 新增缓存用于存储每日的累计值
        Map<String, BigDecimal> previousLjxsCache = new HashMap<>(); // 缓存前一天的累计销售
        Map<String, BigDecimal> previousLjhkCache = new HashMap<>(); // 缓存前一天的累计还款
        Map<String, BigDecimal> qcyeCache = new HashMap<>(); // 缓存期初余额（每个客户只需计算一次）

        try {
            // 开始清理开始时间到结束时间的数据后再进行计算更新数据
            String startDateFormat = startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            String nowDateFormat = nowDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            int delete = DBSql.update("DELETE FROM " + section.getTargetTable() +
                    " WHERE RQ BETWEEN '" + startDateFormat + "' AND '" + nowDateFormat + "'");
            LOGGER.info("已删除目标表[{}]中{}条数据（时间范围: {} - {})",
                    section.getTargetTable(), delete, startDateFormat, nowDateFormat);

            // 查询销售组织和客户分组信息
            List<RowMap> maps = DBSql.getMaps("SELECT QYGS, FCUSTNAME, SQ AS FPROVINCE,CS AS FCITY FROM " +
                    section.getYsdTable() + " WHERE QYGS IS NOT NULL GROUP BY QYGS, FCUSTNAME, SQ,CS");

            if (maps == null || maps.isEmpty()) {
                LOGGER.warn("{}板块未查询到销售组织和客户数据", sectionName);
                return true; // 没有数据也算成功
            }

            LOGGER.info("{}板块共查询到 {} 个销售组织-客户组合", sectionName, maps.size());

            // 用于批量插入的数据列表
            List<BO> batchData = new ArrayList<>();
            int processedCount = 0;

            // 遍历日期范围
            for (LocalDate currentDate = startDate; !currentDate.isAfter(nowDate); currentDate = currentDate.plusDays(1)) {
                LOGGER.debug("{}板块正在处理日期: {}", sectionName, currentDate);

                // 遍历每个销售组织-客户组合
                for (RowMap row : maps) {
                    try {
                        String manageRegionName = row.getString("QYGS");
                        String saleOrgUnit = row.getString("XSZZ");
                        String custName = row.getString("FCUSTNAME");

                        // 查询省市区信息
                        String province = row != null ? row.getString("FPROVINCE") : "";
                        String city = row != null ? row.getString("FCITY") : "";
                        String district = row != null ? row.getString("FDISTRICT")!=null ? row.getString("FDISTRICT"):"": "";

                        String cacheKey = saleOrgUnit + "_" + custName;

                        // 计算期初余额（上一年度）- 每个客户只需计算一次
                        BigDecimal qcye;
                        if (currentDate.equals(startDate)) {
                            LocalDate previousYearStart = currentDate.minusYears(1).withDayOfYear(1);
                            LocalDate previousYearEnd = currentDate.minusYears(1).withDayOfYear(365);
                            qcye = calculateInitialBalance(section, saleOrgUnit, custName, previousYearStart, previousYearEnd);
                            qcyeCache.put(cacheKey, qcye);
                        } else {
                            qcye = qcyeCache.get(cacheKey);
                            if (qcye == null) {
                                // 如果缓存中没有，说明之前没有计算过，则计算并缓存
                                LocalDate previousYearStart = currentDate.minusYears(1).withDayOfYear(1);
                                LocalDate previousYearEnd = currentDate.minusYears(1).withDayOfYear(365);
                                qcye = calculateInitialBalance(section, saleOrgUnit, custName, previousYearStart, previousYearEnd);
                                qcyeCache.put(cacheKey, qcye);
                            }
                        }

                        // 计算当日销售
                        BigDecimal dailySales = calculateTotalSales(section, saleOrgUnit, custName, currentDate, currentDate);
                        // 计算当日还款
                        BigDecimal dailyRepayment = calculateTotalRepayment(section, saleOrgUnit, custName, currentDate, currentDate);

                        // 计算累计销售（本年年初到当前日期）- 使用增量方式
                        BigDecimal ljxs;
                        if (currentDate.equals(startDate)) {
                            // 第一天直接使用当日销售
                            ljxs = dailySales;
                        } else {
                            // 后续天数：前一天的累计销售 + 当日销售
                            BigDecimal previousLjxs = previousLjxsCache.getOrDefault(cacheKey, BigDecimal.ZERO);
                            ljxs = previousLjxs.add(dailySales);
                        }
                        previousLjxsCache.put(cacheKey, ljxs);

                        // 计算累计还款（本年年初到当前日期）- 使用增量方式
                        BigDecimal ljhk;
                        if (currentDate.equals(startDate)) {
                            // 第一天直接使用当日还款
                            ljhk = dailyRepayment;
                        } else {
                            // 后续天数：前一天的累计还款 + 当日还款
                            BigDecimal previousLjhk = previousLjhkCache.getOrDefault(cacheKey, BigDecimal.ZERO);
                            ljhk = previousLjhk.add(dailyRepayment);
                        }
                        previousLjhkCache.put(cacheKey, ljhk);

                        // 计算应收余额
                        BigDecimal ysye = qcye.add(ljxs).subtract(ljhk);

                        // 计算账龄分析
                        Map<String, BigDecimal> zlfx = calculateAgingAnalysis(historicalBalanceCache, saleOrgUnit, custName, currentDate, ysye);

                        // 创建数据记录
                        BO record = createRecord(
                                section, manageRegionName, saleOrgUnit, currentDate, custName,
                                province, city, district, qcye, ljxs, ljhk, ysye, zlfx
                        );

                        batchData.add(record);

                        // 批量插入
                        if (batchData.size() >= BATCH_SIZE) {
                            batchInsert(section, batchData);
                            batchData.clear();
                        }

                        // 更新历史余额缓存，用于后续账龄分析
                        updateHistoricalBalanceCache(historicalBalanceCache, saleOrgUnit, custName, currentDate, ysye);

                        // 更新处理计数
                        processedCount++;
                        if (processedCount % 1000 == 0) {
                            LOGGER.info("{}板块已处理 {} 条记录", sectionName, processedCount);
                        }

                    } catch (Exception e) {
                        LOGGER.error("{}板块处理销售组织 {} 客户 {} 日期 {} 时发生错误",
                                sectionName, row.getString("FSaleOrgUnit"), row.getString("FCustName"), currentDate, e);
                    }
                }
            }

            // 插入剩余数据
            if (!batchData.isEmpty()) {
                batchInsert(section, batchData);
            }

            LOGGER.info("{}板块数据处理完成，共处理 {} 条记录", sectionName, processedCount);
            return true;

        } catch (Exception e) {
            LOGGER.error("处理{}板块数据时发生错误", sectionName, e);
            return false;
        }
    }

    /**
     * 计算期初余额
     */
    private BigDecimal calculateInitialBalance(Section section, String saleOrgUnit, String custName,
                                               LocalDate startDate, LocalDate endDate) {
        try {
            // 查询应收单总额
            RowMap ysResult = DBSql.getMap(
                    "SELECT SUM(SSJERMB) as total FROM " + section.getYsdTable() +
                            " WHERE XSZZ = ? AND FCUSTNAME = ? AND DZRQ BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            BigDecimal ysTotal = ysResult != null && ysResult.get("total") != null ?
                    new BigDecimal(ysResult.get("total").toString()) : BigDecimal.ZERO;

            // 查询收款单总额
            RowMap skResult = DBSql.getMap(
                    "SELECT SUM(FSALEAMOUNTRMB) as total FROM " + section.getSkdTable() +
                            " WHERE FSALEORGUNIT = ? AND FCUSTNAME = ? AND FBILLDATE BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            BigDecimal skTotal = skResult != null && skResult.get("total") != null ?
                    new BigDecimal(skResult.get("total").toString()) : BigDecimal.ZERO;

            // 查询收款退款单总额
            RowMap sktkResult = DBSql.getMap(
                    "SELECT SUM(FSALEAMOUNTRMB) as total FROM " + section.getSktkdTable() +
                            " WHERE FSALEORGUNIT = ? AND FCUSTNAME = ? AND FBILLDATE BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            BigDecimal sktkTotal = sktkResult != null && sktkResult.get("total") != null ?
                    new BigDecimal(sktkResult.get("total").toString()) : BigDecimal.ZERO;

            // 期初余额 = 应收单总额 - 收款单总额 + 收款退款单总额
            return ysTotal.subtract(skTotal).add(sktkTotal);

        } catch (Exception e) {
            LOGGER.error("计算{}板块期初余额时发生错误，销售组织: {}, 客户: {}", section.getName(), saleOrgUnit, custName, e);
            return BigDecimal.ZERO;
        }
    }

    /**
     * 计算累计销售
     */
    private BigDecimal calculateTotalSales(Section section, String saleOrgUnit, String custName,
                                           LocalDate startDate, LocalDate endDate) {
        try {
            RowMap result = DBSql.getMap(
                    "SELECT SUM(SSJERMB) as total FROM " + section.getYsdTable() +
                            " WHERE XSZZ = ? AND FCUSTNAME = ? AND DZRQ BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            return result != null && result.get("total") != null ?
                    new BigDecimal(result.get("total").toString()) : BigDecimal.ZERO;

        } catch (Exception e) {
            LOGGER.error("计算{}板块累计销售时发生错误，销售组织: {}, 客户: {}", section.getName(), saleOrgUnit, custName, e);
            return BigDecimal.ZERO;
        }
    }

    /**
     * 计算累计还款
     */
    private BigDecimal calculateTotalRepayment(Section section, String saleOrgUnit, String custName,
                                               LocalDate startDate, LocalDate endDate) {
        try {
            // 查询收款单总额
            RowMap skResult = DBSql.getMap(
                    "SELECT SUM(FSALEAMOUNTRMB) as total FROM " + section.getSkdTable() +
                            " WHERE FSALEORGUNIT = ? AND FCUSTNAME = ? AND FBILLDATE BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            BigDecimal skTotal = skResult != null && skResult.get("total") != null ?
                    new BigDecimal(skResult.get("total").toString()) : BigDecimal.ZERO;

            // 查询收款退款单总额
            RowMap sktkResult = DBSql.getMap(
                    "SELECT SUM(FSALEAMOUNTRMB) as total FROM " + section.getSktkdTable() +
                            " WHERE FSALEORGUNIT = ? AND FCUSTNAME = ? AND FBILLDATE BETWEEN ? AND ?",
                    saleOrgUnit, custName, java.sql.Date.valueOf(startDate), java.sql.Date.valueOf(endDate)
            );

            BigDecimal sktkTotal = sktkResult != null && sktkResult.get("total") != null ?
                    new BigDecimal(sktkResult.get("total").toString()) : BigDecimal.ZERO;

            // 累计还款 = 收款单总额 - 收款退款单总额
            return skTotal.subtract(sktkTotal);

        } catch (Exception e) {
            LOGGER.error("计算{}板块累计还款时发生错误，销售组织: {}, 客户: {}", section.getName(), saleOrgUnit, custName, e);
            return BigDecimal.ZERO;
        }
    }

    /**
     * 计算账龄分析
     */
    private Map<String, BigDecimal> calculateAgingAnalysis(Map<String, Map<LocalDate, BigDecimal>> historicalBalanceCache,
                                                           String saleOrgUnit, String custName,
                                                           LocalDate currentDate, BigDecimal currentBalance) {
        Map<String, BigDecimal> result = new HashMap<>();

        try {
            String key = saleOrgUnit + "_" + custName;
            Map<LocalDate, BigDecimal> customerHistory = historicalBalanceCache.getOrDefault(key, new HashMap<>());

            // 计算各个时间点的应收余额
            LocalDate date60DaysAgo = currentDate.minusDays(60);
            LocalDate date1YearAgo = currentDate.minusYears(1);
            LocalDate date2YearsAgo = currentDate.minusYears(2);
            LocalDate date3YearsAgo = currentDate.minusYears(3);
            LocalDate date4YearsAgo = currentDate.minusYears(4);
            LocalDate date5YearsAgo = currentDate.minusYears(5);

            BigDecimal balance60DaysAgo = findClosestBalance(customerHistory, date60DaysAgo);
            BigDecimal balance1YearAgo = findClosestBalance(customerHistory, date1YearAgo);
            BigDecimal balance2YearsAgo = findClosestBalance(customerHistory, date2YearsAgo);
            BigDecimal balance3YearsAgo = findClosestBalance(customerHistory, date3YearsAgo);
            BigDecimal balance4YearsAgo = findClosestBalance(customerHistory, date4YearsAgo);
            BigDecimal balance5YearsAgo = findClosestBalance(customerHistory, date5YearsAgo);

            // 计算各账龄段金额
            result.put("ZLFX0_60", currentBalance.subtract(balance60DaysAgo));
            result.put("ZLFX60_1", balance60DaysAgo.subtract(balance1YearAgo));
            result.put("ZLFX1_2", balance1YearAgo.subtract(balance2YearsAgo));
            result.put("ZLFX2_3", balance2YearsAgo.subtract(balance3YearsAgo));
            result.put("ZLFX3_4", balance3YearsAgo.subtract(balance4YearsAgo));
            result.put("ZLFX4_5", balance4YearsAgo.subtract(balance5YearsAgo));
            result.put("ZLFX5", balance5YearsAgo);

        } catch (Exception e) {
            LOGGER.error("计算账龄分析时发生错误，销售组织: {}, 客户: {}", saleOrgUnit, custName, e);
            // 发生错误时设置默认值
            result.put("ZLFX0_60", BigDecimal.ZERO);
            result.put("ZLFX60_1", BigDecimal.ZERO);
            result.put("ZLFX1_2", BigDecimal.ZERO);
            result.put("ZLFX2_3", BigDecimal.ZERO);
            result.put("ZLFX3_4", BigDecimal.ZERO);
            result.put("ZLFX4_5", BigDecimal.ZERO);
            result.put("ZLFX5", BigDecimal.ZERO);
        }

        return result;
    }

    /**
     * 查找最接近日期的应收余额
     */
    private BigDecimal findClosestBalance(Map<LocalDate, BigDecimal> history, LocalDate targetDate) {
        if (history.isEmpty()) {
            return BigDecimal.ZERO;
        }

        // 首先尝试精确匹配
        if (history.containsKey(targetDate)) {
            return history.get(targetDate);
        }

        // 如果没有精确匹配，查找最接近的日期
        LocalDate closestDate = null;
        for (LocalDate date : history.keySet()) {
            if (date.isBefore(targetDate) && (closestDate == null || date.isAfter(closestDate))) {
                closestDate = date;
            }
        }

        return closestDate != null ? history.get(closestDate) : BigDecimal.ZERO;
    }

    /**
     * 创建数据记录
     */
    private BO createRecord(Section section, String manageRegionName, String saleOrgUnit, LocalDate date,
                            String custName, String province, String city, String district,
                            BigDecimal qcye, BigDecimal ljxs, BigDecimal ljhk, BigDecimal ysye,
                            Map<String, BigDecimal> zlfx) {
        BO record = new BO();

        // 业务字段
        record.set("BKGS", section.getBkgs());
        record.set("QYGS", manageRegionName);
        record.set("XSZZ", saleOrgUnit);
        record.set("RQ", Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()));
        record.set("KH", custName);
        record.set("SHENGQU", province);
        record.set("SHIQU", city);
        record.set("QX", district);
        record.set("QCYE", qcye);
        record.set("LJXS", ljxs);
        record.set("LJHK", ljhk);
        record.set("YSYE", ysye);

        // 账龄分析字段
        record.set("ZLFX0_60", zlfx.get("ZLFX0_60"));
        record.set("ZLFX60_1", zlfx.get("ZLFX60_1"));
        record.set("ZLFX1_2", zlfx.get("ZLFX1_2"));
        record.set("ZLFX2_3", zlfx.get("ZLFX2_3"));
        record.set("ZLFX3_4", zlfx.get("ZLFX3_4"));
        record.set("ZLFX4_5", zlfx.get("ZLFX4_5"));
        record.set("ZLFX5", zlfx.get("ZLFX5"));

        return record;
    }

    /**
     * 批量插入数据
     */
    private void batchInsert(Section section, List<BO> batchData) {
        try {
            int[] dataBO = SDK.getBOAPI().createDataBO(section.getTargetTable(), batchData, UserContext.fromUID("admin"));
            long result = Arrays.stream(dataBO).count();
            LOGGER.info("成功插入 {} 条数据到 {} 表", result, section.getTargetTable());
        } catch (Exception e) {
            LOGGER.error("批量插入{}板块数据到表{}时发生错误", section.getName(), section.getTargetTable(), e);
        }
    }

    /**
     * 更新历史余额缓存
     */
    private void updateHistoricalBalanceCache(Map<String, Map<LocalDate, BigDecimal>> historicalBalanceCache,
                                              String saleOrgUnit, String custName,
                                              LocalDate date, BigDecimal balance) {
        String key = saleOrgUnit + "_" + custName;
        historicalBalanceCache
                .computeIfAbsent(key, k -> new HashMap<>())
                .put(date, balance);
    }
}