package com.awspaas.user.apps.donenow_ivt.service;

import com.actionsoft.bpms.bo.engine.BO;
import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
import com.actionsoft.bpms.commons.database.RowMap;
import com.actionsoft.bpms.server.UserContext;
import com.actionsoft.bpms.util.DBSql;
import com.actionsoft.bpms.util.TypeUtil;
import com.actionsoft.sdk.local.SDK;
import com.actionsoft.sdk.local.api.Logger;
import com.awspaas.user.apps.donenow_ivt.constant.IVTConstant;
import com.awspaas.user.apps.donenow_ivt.utils.PeriodCalculationUtil;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Connection;
import java.sql.SQLException;
import java.time.LocalDate;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 付款计划服务
 */
public class PaymentPlanService {
    // 常量定义
    private static final Logger logger = SDK.getLogAPI().getLogger(PaymentPlanService.class);

    // 单例模式
    private static volatile PaymentPlanService instance;

    private PaymentPlanService() {
    }

    public static PaymentPlanService getInstance() {
        if (instance == null) {
            synchronized (PaymentPlanService.class) {
                if (instance == null) {
                    instance = new PaymentPlanService();
                }
            }
        }
        return instance;
    }


    /**
     * 创建付款计划
     * @param uc
     * @param orderId
     */
    public void createPaymentPlan(UserContext uc, String orderId) {
        BO purchaseOrder = SDK.getBOAPI().get("BO_EU_DNIVT_ORDER", orderId);
        List<BO> orderProducts = SDK.getBOAPI().query("BO_EU_DNIVT_ORDER_PRODUCT").bindId(purchaseOrder.getBindId()).list();
        String VENDOR_ACCOUNT_ID = purchaseOrder.getString("VENDOR_ACCOUNT_ID");//供应商账户ID
        List<String> serviceIds = new ArrayList<>();
        Connection conn = null;
        try {
            conn = DBSql.open();
            conn.setAutoCommit(false);

            int total = 0;
            boolean isOnlyOne = true;
            for (BO orderProduct : orderProducts) {
                if (StringUtils.isNotBlank(orderProduct.getString("CONTRACT_COST_ID"))) {
                    String SERVICE_ID = DBSql.getString(conn, "select SERVICE_ID from BO_EU_DNCTT_CONTRACT_COST where ID=?", new Object[]{orderProduct.getString("CONTRACT_COST_ID")});
                    //关联服务
                    if (StringUtils.isNotBlank(SERVICE_ID)) {
                        isOnlyOne = false;

                        //跳过做过服务调整的
                        if (DBSql.getInt(conn, "SELECT COUNT(1) CNT FROM BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST WHERE CONTRACT_SERVICE_ID=? ", new Object[]{SERVICE_ID}) > 0) {
                            continue;
                        }

                        createServicePaymentPlan(uc, orderId, SERVICE_ID, VENDOR_ACCOUNT_ID, orderProduct, "BO_EU_DNIVT_ORDER_PAYMENT_PLAN", conn);
                        serviceIds.add(SERVICE_ID);
                    }
                }
            }

            if (isOnlyOne) {
                total += 1;
                String sql = "delete from BO_EU_DNIVT_ORDER_PAYMENT_PLAN where ORDER_ID = ? and (PAY_AMOUNT is null or PAY_AMOUNT = 0)";
                DBSql.update(conn, sql, new Object[]{orderId});
                //全部采购项 合并生成一条采购计划
                ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance("obj_5cb4ae4a42944fd0a9a284ff4c64c65d", uc.getUID(), "付款计划");

                String PLAN_DATE = null;
                if (purchaseOrder.get("EXPECTED_SHIP_DATE") != null)
                    PLAN_DATE = purchaseOrder.get("EXPECTED_SHIP_DATE").toString().substring(0, 10);
                else PLAN_DATE = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

                BigDecimal PLAN_AMOUNT = orderProducts.stream().map(orderProduct -> orderProduct.get("COST_TOTAL", BigDecimal.class)).reduce(BigDecimal.ZERO, BigDecimal::add);

                String PAY_DESC = orderProducts.stream().map(orderProduct -> orderProduct.getString("NAME")).collect(Collectors.joining(","));

                if (StringUtils.isNotBlank(PAY_DESC) && PAY_DESC.length() > 300)
                    PAY_DESC = PAY_DESC.substring(0, 300);

                BO paymentPlan = new BO();
                paymentPlan.setBindId(processInstance.getId());
                paymentPlan.set("ORDER_ID", orderId);
                paymentPlan.set("PLAN_DATE", PLAN_DATE);
                paymentPlan.set("PLAN_AMOUNT", PLAN_AMOUNT);
                paymentPlan.set("REMAIN_AMOUNT", PLAN_AMOUNT);
                paymentPlan.set("PAY_DESC", PAY_DESC);
                paymentPlan.set("ACCOUNT_PAYEE", VENDOR_ACCOUNT_ID);//收款单位
                SDK.getBOAPI().create("BO_EU_DNIVT_ORDER_PAYMENT_PLAN", paymentPlan, processInstance, uc, conn);
                System.out.println("==== 创建付款计划：" + paymentPlan.get("PLAN_DATE") + "，金额：" + paymentPlan.get("PLAN_AMOUNT") + " ====");
            }
            logger.info("付款计划生成完成，生成付款计划数量：" + total);
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException ex) {
                throw new RuntimeException(ex);
            }
        } finally {
            if (conn != null) DBSql.close(conn);
        }

        if (serviceIds.size() > 0)
            for (String SERVICE_ID : serviceIds)
                paySplitServiceMonth(uc, SERVICE_ID);

    }


    public void createServicePaymentPlan(UserContext uc, String orderId, String SERVICE_ID, String VENDOR_ACCOUNT_ID, BO orderProduct, String boName, Connection conn) {

        logger.info("生成付款计划SERVICE_ID--" + SERVICE_ID);
        BO service = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE", SERVICE_ID);
        String RULE_CATE = service.getString("PURCHASE_PERIOD_TYPE");//采购周期


        int periodMonths;
        switch (service.getString("PURCHASE_PERIOD_TYPE")) {
            case "610":
                periodMonths = 1; // 按月
                break;
            case "611":
                periodMonths = 3; // 按季度
                break;
            case "612":
                periodMonths = 6; // 按半年
                break;
            case "613":
                periodMonths = 12; // 按年
                break;
            default:
                periodMonths = 1;
                break;
        }

        if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH") && !RULE_CATE.equals("609"))//按月采购
            RULE_CATE = "610";

        LocalDate PERIOD_BEGIN_DATE = service.get("PURCHASE_START_DATE", LocalDate.class);
        BigDecimal TOTAL_COST = toBigDecimal(service.get("TOTAL_COST"));//周期成本


        if (RULE_CATE.equals("609")) {   //一次性付费

            String sql = "delete from " + boName + " where ORDER_ID = ? and CONTRACT_COST_ID=? and CONTRACT_SERVICE_ID=? and (PAY_AMOUNT is null or PAY_AMOUNT = 0)";
            DBSql.update(conn, sql, new Object[]{orderId, orderProduct.getString("CONTRACT_COST_ID"), SERVICE_ID});

            BO paymentPlan = new BO();
            paymentPlan.set("ORDER_ID", orderId);
            paymentPlan.set("PLAN_DATE", PERIOD_BEGIN_DATE);
            paymentPlan.set("PLAN_AMOUNT", TOTAL_COST);
            paymentPlan.set("REMAIN_AMOUNT", paymentPlan.get("PLAN_AMOUNT"));
            paymentPlan.set("CONTRACT_COST_ID", orderProduct.getString("CONTRACT_COST_ID"));
            paymentPlan.set("CONTRACT_SERVICE_ID", service.getId());
            paymentPlan.set("PAY_DESC", orderProduct.getString("NAME"));
            paymentPlan.set("ACCOUNT_PAYEE", VENDOR_ACCOUNT_ID);//收款单位

            if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {
                ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance("obj_5cb4ae4a42944fd0a9a284ff4c64c65d", uc.getUID(), "付款计划");
                paymentPlan.setBindId(processInstance.getId());
                SDK.getBOAPI().create("BO_EU_DNIVT_ORDER_PAYMENT_PLAN", paymentPlan, processInstance, uc, conn);
            } else {
                SDK.getBOAPI().createDataBO(boName, paymentPlan, uc, conn);
            }

            //一次性收费
            DBSql.update(conn, "UPDATE bo_eu_dnctt_contract_service_period a SET a.PERIOD_COST =( SELECT SUM(b.PLAN_AMOUNT) FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b WHERE a.CONTRACT_SERVICE_ID=? AND b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID) WHERE a.CONTRACT_SERVICE_ID=? AND EXISTS ( SELECT 1 FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b JOIN bo_eu_dnctt_contract_service c ON b.CONTRACT_SERVICE_ID = c.ID WHERE b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID AND c.PERIOD_TYPE = 609 AND c.ID=?)", new Object[]{SERVICE_ID, SERVICE_ID, SERVICE_ID});

            if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {
                createServicePaymentPlan(uc, orderId, SERVICE_ID, VENDOR_ACCOUNT_ID, orderProduct, "BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH", conn);
                //paySplitServiceMonth(uc, SERVICE_ID);
            }

            return;
        }


        LocalDate START_DATE = PERIOD_BEGIN_DATE;
        LocalDate PERIOD_END_DATE = service.get("PURCHASE_END_DATE", LocalDate.class);//新增采购结束时间


        String PURCHASE_CALC_METHOD_1STPERIOD = service.getString("PURCHASE_CALC_METHOD_1STPERIOD");

        if (StringUtils.isBlank(RULE_CATE) || StringUtils.isBlank(PURCHASE_CALC_METHOD_1STPERIOD)) {
            return;
        }

        if (PERIOD_BEGIN_DATE == null || PERIOD_END_DATE == null || PERIOD_BEGIN_DATE.isAfter(PERIOD_END_DATE)) {
            return;
        }


        if (DBSql.getInt(conn, "select count(1) as cnt from  " + boName + " where ORDER_ID = ? and CONTRACT_COST_ID=? and CONTRACT_SERVICE_ID=?  AND PAY_AMOUNT is NOT null AND PAY_AMOUNT<>0", new Object[]{orderId, orderProduct.getString("CONTRACT_COST_ID"), SERVICE_ID}) > 0) {
            return;
        }

        String sql = "delete from " + boName + " where ORDER_ID = ? and CONTRACT_COST_ID=? and CONTRACT_SERVICE_ID=? and (PAY_AMOUNT is null or PAY_AMOUNT = 0)";
        DBSql.update(conn, sql, new Object[]{orderId, orderProduct.getString("CONTRACT_COST_ID"), SERVICE_ID});

        BigDecimal UNIT_COST_1STMONTH = BigDecimal.ZERO;
        String firstPeriodBeginDate = null;

        //首月单独计算
        if (PURCHASE_CALC_METHOD_1STPERIOD.equals("4598") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4595") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4592")) {
            //签约之日开始算周期，则不需要

        } else {
            UNIT_COST_1STMONTH = toBigDecimal(service.get("UNIT_COST_1STMONTH"));

            //按月收费，且从1号开始
            if (PERIOD_BEGIN_DATE.getDayOfMonth() == 1 && RULE_CATE.equals("610") && boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {

                firstPeriodBeginDate = PERIOD_BEGIN_DATE.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                //下月一号
                PERIOD_BEGIN_DATE = PERIOD_BEGIN_DATE.plusMonths(1).withDayOfMonth(1);

                BO paymentPlan = new BO();
                paymentPlan.set("ORDER_ID", orderId);
                paymentPlan.set("PLAN_DATE", firstPeriodBeginDate);
                paymentPlan.set("PERIOD_BEGIN_DATE", firstPeriodBeginDate);
                paymentPlan.set("PERIOD_END_DATE", PERIOD_BEGIN_DATE.minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                paymentPlan.set("PLAN_AMOUNT", UNIT_COST_1STMONTH);
                paymentPlan.set("REMAIN_AMOUNT", paymentPlan.get("PLAN_AMOUNT"));
                paymentPlan.set("CONTRACT_COST_ID", orderProduct.getString("CONTRACT_COST_ID"));
                paymentPlan.set("CONTRACT_SERVICE_ID", service.getId());
                paymentPlan.set("PAY_DESC", orderProduct.getString("NAME"));
                paymentPlan.set("ACCOUNT_PAYEE", VENDOR_ACCOUNT_ID);//收款单位

                ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance("obj_5cb4ae4a42944fd0a9a284ff4c64c65d", uc.getUID(), "付款计划");
                paymentPlan.setBindId(processInstance.getId());
                SDK.getBOAPI().create("BO_EU_DNIVT_ORDER_PAYMENT_PLAN", paymentPlan, processInstance, uc, conn);

                UNIT_COST_1STMONTH = BigDecimal.ZERO;

            } else {
                // 计算“日期当天到当月月底”的天数占当月总天数的比例（2025-6-25 对应 6/30）
                BigDecimal ratio = new BigDecimal(PERIOD_BEGIN_DATE.lengthOfMonth() - PERIOD_BEGIN_DATE.getDayOfMonth() + 1)
                        .divide(new BigDecimal(PERIOD_BEGIN_DATE.lengthOfMonth()), 10, BigDecimal.ROUND_HALF_UP);

                UNIT_COST_1STMONTH = ratio.multiply(UNIT_COST_1STMONTH);
                firstPeriodBeginDate = PERIOD_BEGIN_DATE.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                //下月一号
                PERIOD_BEGIN_DATE = PERIOD_BEGIN_DATE.plusMonths(1).withDayOfMonth(1);
            }

            //首月单独一条
            if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH") && !RULE_CATE.equals("609")) {
                BO paymentPlan = new BO();
                paymentPlan.set("ORDER_ID", orderId);
                paymentPlan.set("PLAN_AMOUNT", UNIT_COST_1STMONTH);
                paymentPlan.set("PLAN_DATE", firstPeriodBeginDate);
                paymentPlan.set("PERIOD_BEGIN_DATE", firstPeriodBeginDate);
                paymentPlan.set("PERIOD_END_DATE", PERIOD_BEGIN_DATE.minusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                paymentPlan.set("REMAIN_AMOUNT", paymentPlan.get("PLAN_AMOUNT"));
                paymentPlan.set("CONTRACT_COST_ID", orderProduct.getString("CONTRACT_COST_ID"));
                paymentPlan.set("CONTRACT_SERVICE_ID", service.getId());
                paymentPlan.set("PAY_DESC", orderProduct.getString("NAME"));
                paymentPlan.set("ACCOUNT_PAYEE", VENDOR_ACCOUNT_ID);//收款单位

                SDK.getBOAPI().createDataBO(boName, paymentPlan, uc, conn);
                UNIT_COST_1STMONTH = BigDecimal.ZERO;
            }
        }

        List<PeriodCalculationUtil.Period> periods = PeriodCalculationUtil.getPeriodListByConfig(RULE_CATE, START_DATE, PERIOD_BEGIN_DATE, PERIOD_END_DATE, PURCHASE_CALC_METHOD_1STPERIOD, service.getString("CALC_METHOD_1STYEAR"), service.getString("MONTH_1STQUARTER"));

        //拆成每月成本
        if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH"))
            TOTAL_COST = TOTAL_COST.divide(new BigDecimal(periodMonths), 10, BigDecimal.ROUND_HALF_UP);

        int i = 0;
        for (PeriodCalculationUtil.Period period : periods) {
            i++;

            BO paymentPlan = new BO();

            paymentPlan.set("ORDER_ID", orderId);
            paymentPlan.set("PLAN_DATE", period.getPeriodBeginDateStr());
            paymentPlan.set("PERIOD_BEGIN_DATE", period.getPeriodBeginDateStr());
            paymentPlan.set("PERIOD_END_DATE", period.getPeriodEndDateStr());

            //第一条周期要加上首月成本
            if (i == 1 && UNIT_COST_1STMONTH.compareTo(BigDecimal.ZERO) > 0) {
                paymentPlan.set("PLAN_AMOUNT", TOTAL_COST.multiply(period.getRate()).add(UNIT_COST_1STMONTH));
                paymentPlan.set("PLAN_DATE", firstPeriodBeginDate);
                paymentPlan.set("PERIOD_BEGIN_DATE", firstPeriodBeginDate);
            } else {
                paymentPlan.set("PLAN_AMOUNT", TOTAL_COST.multiply(period.getRate()));
            }

            paymentPlan.set("REMAIN_AMOUNT", paymentPlan.get("PLAN_AMOUNT"));
            paymentPlan.set("CONTRACT_COST_ID", orderProduct.getString("CONTRACT_COST_ID"));
            paymentPlan.set("CONTRACT_SERVICE_ID", service.getId());
            paymentPlan.set("PAY_DESC", orderProduct.getString("NAME"));
            paymentPlan.set("ACCOUNT_PAYEE", VENDOR_ACCOUNT_ID);//收款单位

            if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {
                ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance("obj_5cb4ae4a42944fd0a9a284ff4c64c65d", uc.getUID(), "付款计划");
                paymentPlan.setBindId(processInstance.getId());
                SDK.getBOAPI().create("BO_EU_DNIVT_ORDER_PAYMENT_PLAN", paymentPlan, processInstance, uc, conn);
            } else {
                SDK.getBOAPI().createDataBO(boName, paymentPlan, uc, conn);
            }

            System.out.println("==== 创建付款计划：" + paymentPlan.get("PLAN_DATE") + "，金额：" + paymentPlan.get("PLAN_AMOUNT") + " ====");
        }

        if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {
            createServicePaymentPlan(uc, orderId, SERVICE_ID, VENDOR_ACCOUNT_ID, orderProduct, "BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH", conn);
        }

    }

    /**
     * 分摊采购成本
     * 将合同服务的采购成本按照服务周期进行分摊。对于一次性收费的服务，
     * 直接将所有付款计划金额汇总后赋给唯一的服务周期；对于其他类型服务，
     * 按照服务周期的时间范围匹配对应的付款计划，并计算该周期内的总费用。
     *
     * @param uc 当前用户上下文信息
     * @param contractServiceId 合同服务ID，用于查询相关服务、付款计划和服务周期数据
     */
    //合同服务采购成本平摊到服务周期上面
    public void paySplitServiceMonth(UserContext uc, String contractServiceId) {

        RowMap service = DBSql.getMap("select * from BO_EU_DNCTT_CONTRACT_SERVICE where ID=?", new Object[]{contractServiceId});

        // 查询与该合同服务关联的所有付款计划，并按开始时间排序
        List<RowMap> payPlanList = DBSql.getMaps("select * from BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});

        if (payPlanList.isEmpty() || payPlanList.size() == 0) return;

        // 查询与该合同服务对应的服务周期记录，并按开始时间排序
        List<RowMap> contractServicePeriodList = DBSql.getMaps("select * from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});

        if (contractServicePeriodList.isEmpty() || contractServicePeriodList.size() == 0) return;

        // 处理“一次性收费”类型的合同服务：所有付款计划总和直接赋给第一个服务周期的成本
        if (service.getString("PERIOD_TYPE").equals("609")) {
            BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;
            for (RowMap payPlan : payPlanList) {

                SUM_PLAN_AMOUNT = SUM_PLAN_AMOUNT.add(TypeUtil.convert(payPlan.get("PLAN_AMOUNT"), BigDecimal.class));
            }

            DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD set PERIOD_COST=? where ID=? ", new Object[]{SUM_PLAN_AMOUNT, contractServicePeriodList.get(0).getString("ID")});
            return;
        }

        LocalDate LAST_DATE = null;
        for (RowMap servicePeriod : contractServicePeriodList) {
            // 获取当前服务周期的结束日期
            LocalDate PERIOD_END_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_END_DATE"), LocalDate.class);
            SDK.getLogAPI().consoleInfo("PERIOD_END_DATE：" + PERIOD_END_DATE.toString());

            BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;

            for (RowMap payPlan : payPlanList) {
                LocalDate PAY_PERIOD_BEGIN_DATE = TypeUtil.convert(payPlan.get("PERIOD_BEGIN_DATE"), LocalDate.class);

                SDK.getLogAPI().consoleInfo("PAY_PERIOD_BEGIN_DATE：" + PAY_PERIOD_BEGIN_DATE.toString());

                // 跳过已处理过的付款计划（通过上一个周期末日期+1判断）
                if (LAST_DATE != null && PAY_PERIOD_BEGIN_DATE.isBefore(LAST_DATE)) {
                    continue;
                }

                // 若付款计划起始日在当前服务周期之后，则跳过
                if (PERIOD_END_DATE.isBefore(PAY_PERIOD_BEGIN_DATE)) {
                    continue;
                }

                SDK.getLogAPI().consoleInfo("PLAN_AMOUNT ： " + payPlan.getString("PLAN_AMOUNT"));

                // 累加符合时间段内的付款计划金额
                SUM_PLAN_AMOUNT = SUM_PLAN_AMOUNT.add(TypeUtil.convert(payPlan.get("PLAN_AMOUNT"), BigDecimal.class));

            }
            // 更新当前服务周期的成本字段
            DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD set PERIOD_COST=? where ID=? ", new Object[]{SUM_PLAN_AMOUNT, servicePeriod.getString("ID")});

            SDK.getLogAPI().consoleInfo("服务周期：" + servicePeriod.getString("ID") + ":" + SUM_PLAN_AMOUNT);

            // 设置下一轮比较的时间起点为当前周期结束后一天
            LAST_DATE = PERIOD_END_DATE.plusDays(1);
        }
    }


    /**
     * 获取周期列表
     * @param RULE_CATE
     * @param CONTRACT_START_DATE
     * @param PERIOD_BEGIN_DATE
     * @param PERIOD_END_DATE
     * @param isNaturalMonth //自然月和实际月
     * @return
     */
    private List<PERIOD> getPeriodList(String RULE_CATE, LocalDate CONTRACT_START_DATE, LocalDate PERIOD_BEGIN_DATE, LocalDate PERIOD_END_DATE, boolean isNaturalMonth) {

        logger.info("获取周期列表");
        logger.info("RULE_CATE:" + RULE_CATE);
        logger.info("CONTRACT_START_DATE:" + CONTRACT_START_DATE);
        logger.info("PERIOD_BEGIN_DATE:" + PERIOD_BEGIN_DATE);
        logger.info("PERIOD_END_DATE:" + PERIOD_END_DATE);
        logger.info("isNaturalMonth:" + isNaturalMonth);

        if (RULE_CATE.equals(IVTConstant.PERIOD_TYPE_MONTH)) {
            isNaturalMonth = true;
        } else {
            isNaturalMonth = false;
        }

        if (CONTRACT_START_DATE.getDayOfMonth() != 1)// 不是1号开始，就从1号开始
            CONTRACT_START_DATE = CONTRACT_START_DATE.with(TemporalAdjusters.firstDayOfMonth());

        List<PERIOD> periodList = new ArrayList<>();
        //一次性收费
        if (RULE_CATE.equals(IVTConstant.PERIOD_TYPE_ONE_TIME)) {
            PERIOD period = new PERIOD();
            period.setPeriodBeginDate(PERIOD_BEGIN_DATE);
            period.setPeriodEndDate(PERIOD_END_DATE);
            period.setRate(BigDecimal.ONE);
            periodList.add(period);
        } else {
            LocalDate START_DATE = null;
            LocalDate currLastDay = null;//当前周期的结束日
            int periodDays = 0;//当前周期天数
            switch (RULE_CATE) {
                case IVTConstant.PERIOD_TYPE_MONTH://按月支付
                {
                    if (isNaturalMonth) {
                        //自然月
                        if (PERIOD_BEGIN_DATE.getDayOfMonth() != 1) {
                            currLastDay = PERIOD_BEGIN_DATE.with(TemporalAdjusters.lastDayOfMonth());
                            periodDays = PERIOD_BEGIN_DATE.lengthOfMonth();
                        }
                    } else {
                        //实际月
                        if (CONTRACT_START_DATE.isAfter(PERIOD_BEGIN_DATE))//合同开始日期在 服务开始日期之后
                        {
                            currLastDay = CONTRACT_START_DATE.minusDays(1);//获取合同开始日期的前一日
                            periodDays = GetPeriodDays(CONTRACT_START_DATE.minusMonths(1), currLastDay);

                        } else if (CONTRACT_START_DATE.isBefore(PERIOD_BEGIN_DATE)) {//合同开始日期在 服务开始日期之前
                            currLastDay = CONTRACT_START_DATE.plusMonths(1).minusDays(1);

                            periodDays = GetPeriodDays(CONTRACT_START_DATE, currLastDay);

                            if (currLastDay.isAfter(PERIOD_END_DATE))//如果当前周期结束日大于服务结束日
                            {
                                currLastDay = PERIOD_END_DATE;
                            }

                        } else {


                        }
                    }
                }
                break;
                case IVTConstant.PERIOD_TYPE_QUARTER://按季支付
                {
                    if (isNaturalMonth) {
                        //如果不是季度第一天
                        if (PERIOD_BEGIN_DATE.getDayOfMonth() != 1 || (PERIOD_BEGIN_DATE.getMonthValue() - 1) % 3 != 0) {
                            Month endMonth = getQuarterEndMonth(PERIOD_BEGIN_DATE.getMonth());
                            currLastDay = LocalDate.of(PERIOD_BEGIN_DATE.getYear(), endMonth, endMonth.length(PERIOD_BEGIN_DATE.isLeapYear()) // 考虑闰年2月的天数
                            );
                            periodDays = getDaysInCurrentQuarter(PERIOD_BEGIN_DATE);
                        }
                    } else {
                        //实际月
                        if (CONTRACT_START_DATE.isAfter(PERIOD_BEGIN_DATE))//合同开始日期在 服务开始日期之后
                        {
                            currLastDay = CONTRACT_START_DATE.minusDays(1);//获取合同开始日期的前一日
                            periodDays = GetPeriodDays(CONTRACT_START_DATE.minusMonths(3), currLastDay);

                        } else if (CONTRACT_START_DATE.isBefore(PERIOD_BEGIN_DATE)) {//合同开始日期在 服务开始日期之前
                            currLastDay = CONTRACT_START_DATE.plusMonths(3).minusDays(1);

                            periodDays = GetPeriodDays(CONTRACT_START_DATE, currLastDay);

                            if (currLastDay.isAfter(PERIOD_END_DATE))//如果当前周期结束日大于服务结束日
                            {
                                currLastDay = PERIOD_END_DATE;
                            }

                        } else {


                        }
                    }

                }
                break;
                case IVTConstant.PERIOD_TYPE_HALFYEAR://按半年支付
                    if (isNaturalMonth) {
                        // 若不是半年第一天，计算本半年剩余天数作为首个周期
                        if (PERIOD_BEGIN_DATE.getDayOfMonth() != 1 || (PERIOD_BEGIN_DATE.getMonthValue() - 1) % 6 != 0) {
                            Month endMonth = getHalfYearEndMonth(PERIOD_BEGIN_DATE.getMonth());
                            currLastDay = LocalDate.of(PERIOD_BEGIN_DATE.getYear(), endMonth, endMonth.length(PERIOD_BEGIN_DATE.isLeapYear()));
                            periodDays = getDaysInCurrentHalfYear(PERIOD_BEGIN_DATE);
                        }
                    } else {
                        //实际月
                        if (CONTRACT_START_DATE.isAfter(PERIOD_BEGIN_DATE))//合同开始日期在 服务开始日期之后
                        {
                            currLastDay = CONTRACT_START_DATE.minusDays(1);//获取合同开始日期的前一日
                            periodDays = GetPeriodDays(CONTRACT_START_DATE.minusMonths(6), currLastDay);

                        } else if (CONTRACT_START_DATE.isBefore(PERIOD_BEGIN_DATE)) {//合同开始日期在 服务开始日期之前
                            currLastDay = CONTRACT_START_DATE.plusMonths(6).minusDays(1);

                            periodDays = GetPeriodDays(CONTRACT_START_DATE, currLastDay);

                            if (currLastDay.isAfter(PERIOD_END_DATE))//如果当前周期结束日大于服务结束日
                            {
                                currLastDay = PERIOD_END_DATE;
                            }

                        } else {


                        }
                    }
                    break;
                case IVTConstant.PERIOD_TYPE_YEAR://按年支付
                {
                    if (isNaturalMonth) {
                        //如果不是年第一天
                        if (PERIOD_BEGIN_DATE.getDayOfMonth() != 1 || PERIOD_BEGIN_DATE.getMonthValue() != 1) {
                            //所在年最后一天
                            currLastDay = LocalDate.of(PERIOD_BEGIN_DATE.getYear(), 12, 31);
                            periodDays = PERIOD_BEGIN_DATE.lengthOfYear();
                        }

                    } else {
                        //实际月
                        if (CONTRACT_START_DATE.isAfter(PERIOD_BEGIN_DATE))//合同开始日期在 服务开始日期之后
                        {
                            currLastDay = CONTRACT_START_DATE.minusDays(1);//获取合同开始日期的前一日
                            periodDays = GetPeriodDays(CONTRACT_START_DATE.minusMonths(12), currLastDay);

                        } else if (CONTRACT_START_DATE.isBefore(PERIOD_BEGIN_DATE)) {//合同开始日期在 服务开始日期之前
                            currLastDay = CONTRACT_START_DATE.plusMonths(12).minusDays(1);

                            periodDays = GetPeriodDays(CONTRACT_START_DATE, currLastDay);

                            if (currLastDay.isAfter(PERIOD_END_DATE))//如果当前周期结束日大于服务结束日
                            {
                                currLastDay = PERIOD_END_DATE;
                            }
                        } else {


                        }
                    }
                }
                break;
                default: {
                    //报错，类型异常
                    return null;
                }
            }
            logger.info("currLastDay:" + currLastDay);
            logger.info("periodDays:" + periodDays);

            if (currLastDay != null) {
                if (currLastDay.isBefore(PERIOD_END_DATE)) {
                    PERIOD period = new PERIOD();
                    period.setPeriodBeginDate(PERIOD_BEGIN_DATE);
                    period.setPeriodEndDate(currLastDay);

                    BigDecimal periodRate = divideToBigDecimal(GetPeriodDays(PERIOD_BEGIN_DATE, currLastDay), periodDays);
                    period.setRate(periodRate);

                    periodList.add(period);

                    START_DATE = currLastDay.plusDays(1);//周期开始日期
                } else {
                    PERIOD period = new PERIOD();
                    period.setPeriodBeginDate(PERIOD_BEGIN_DATE);
                    period.setPeriodEndDate(PERIOD_END_DATE);

                    BigDecimal periodRate = divideToBigDecimal(GetPeriodDays(PERIOD_BEGIN_DATE, PERIOD_END_DATE), periodDays);
                    period.setRate(periodRate);

                    periodList.add(period);
                }
            } else {
                START_DATE = PERIOD_BEGIN_DATE;
            }

            logger.info("START_DATE:" + START_DATE);

            //循环
            if (START_DATE != null && START_DATE.isBefore(PERIOD_END_DATE)) {

                while (!START_DATE.isAfter(PERIOD_END_DATE)) {
                    LocalDate periodEnd = getNextPeriodStart(START_DATE, RULE_CATE).minusDays(1);//本周期结束时间

                    BigDecimal periodRate = BigDecimal.ONE;

                    if (periodEnd.isAfter(PERIOD_END_DATE)) {//如果结束日期大于合同结束日期，则计算结束日期和合同结束日期的差值，并计算该差值占整周期的比例
                        periodRate = divideToBigDecimal(GetPeriodDays(START_DATE, PERIOD_END_DATE), GetPeriodDays(START_DATE, periodEnd));
                        periodEnd = PERIOD_END_DATE;
                    }

                    PERIOD period = new PERIOD();
                    period.setPeriodBeginDate(START_DATE);
                    period.setPeriodEndDate(periodEnd);
                    period.setRate(periodRate);
                    periodList.add(period);

                    if (periodEnd != null) {
                        START_DATE = periodEnd.plusDays(1);
                    }
                }
            }
        }
        return periodList;
    }

    /**
     * 获取下一周期开始时间
     * @param start 当前周期开始时间
     * @param periodType 周期类型
     * @return 下一周期开始时间
     */
    public static LocalDate getNextPeriodStart(LocalDate start, String periodType) {
        // 根据不同的周期类型计算下一周期的开始时间
        switch (periodType) {
            case IVTConstant.PERIOD_TYPE_MONTH:
                // 如果是月周期，则在当前时间基础上增加1个月
                return start.plus(1, ChronoUnit.MONTHS);
            case IVTConstant.PERIOD_TYPE_QUARTER:
                // 如果是季度周期，则在当前时间基础上增加3个月
                return start.plus(3, ChronoUnit.MONTHS);
            case IVTConstant.PERIOD_TYPE_HALFYEAR:
                // 如果是半年周期，则在当前时间基础上增加6个月
                return start.plus(6, ChronoUnit.MONTHS);
            case IVTConstant.PERIOD_TYPE_YEAR:
                // 如果是年周期，则在当前时间基础上增加1年
                return start.plus(1, ChronoUnit.YEARS);
            default:
                // 如果周期类型不匹配任何已知类型，则返回原始开始时间
                return start;
        }
    }

    /**
     * 获取当前季度的结束月份
     */
    private static Month getQuarterEndMonth(Month month) {
        int monthValue = month.getValue();
        if (monthValue <= 3) {
            return Month.MARCH;   // 第一季度结束月
        } else if (monthValue <= 6) {
            return Month.JUNE;    // 第二季度结束月
        } else if (monthValue <= 9) {
            return Month.SEPTEMBER; // 第三季度结束月
        } else {
            return Month.DECEMBER;  // 第四季度结束月
        }
    }

    /**
     * 计算当前季度的总天数
     */
    private static int getDaysInCurrentQuarter(LocalDate date) {
        // 获取当前月份
        Month currentMonth = date.getMonth();
        // 确定当前季度的起始月份和结束月份
        Month startMonth = getQuarterStartMonth(currentMonth);
        Month endMonth = getQuarterEndMonth(currentMonth);

        // 构建季度的第一天（当月第一天）
        LocalDate quarterStart = LocalDate.of(date.getYear(), startMonth, 1);

        // 构建季度的最后一天（当月最后一天）
        LocalDate quarterEnd = LocalDate.of(date.getYear(), endMonth, endMonth.length(date.isLeapYear()) // 考虑闰年2月的天数
        );

        // 计算两个日期之间的天数（包含首尾两天）
        return (int) ChronoUnit.DAYS.between(quarterStart, quarterEnd) + 1;
    }

    /**
     * 获取当前季度的起始月份
     */
    private static Month getQuarterStartMonth(Month month) {
        int monthValue = month.getValue();
        if (monthValue <= 3) {
            return Month.JANUARY; // 第一季度：1-3月
        } else if (monthValue <= 6) {
            return Month.APRIL;   // 第二季度：4-6月
        } else if (monthValue <= 9) {
            return Month.JULY;    // 第三季度：7-9月
        } else {
            return Month.OCTOBER; // 第四季度：10-12月
        }
    }

    /**
     * 获取当前半年的结束月份
     */
    private static Month getHalfYearEndMonth(Month month) {
        int monthValue = month.getValue();
        return monthValue <= 6 ? Month.JUNE : Month.DECEMBER;
    }

    /**
     * 获取当前半年的起始月份
     */
    private static Month getHalfYearStartMonth(Month month) {
        int monthValue = month.getValue();
        return monthValue <= 6 ? Month.JANUARY : Month.JULY;
    }

    /**
     * 计算当前半年的总天数
     */
    private static int getDaysInCurrentHalfYear(LocalDate date) {
        Month currentMonth = date.getMonth();
        Month startMonth = getHalfYearStartMonth(currentMonth);
        Month endMonth = getHalfYearEndMonth(currentMonth);

        LocalDate halfYearStart = LocalDate.of(date.getYear(), startMonth, 1);
        LocalDate halfYearEnd = LocalDate.of(date.getYear(), endMonth, endMonth.length(date.isLeapYear()));

        return GetPeriodDays(halfYearStart, halfYearEnd);
    }

    private static int GetPeriodDays(LocalDate start, LocalDate end) {
        return (int) ChronoUnit.DAYS.between(start, end) + 1;
    }

    // 定义一个方法，将两个 int 相除并返回 BigDecimal
    public static BigDecimal divideToBigDecimal(int numerator, int denominator) {
        if (denominator == 0) {
            throw new ArithmeticException("分母不能为零");
        }

        // 使用 BigDecimal 进行精确计算
        BigDecimal num = new BigDecimal(numerator);
        BigDecimal den = new BigDecimal(denominator);

        // 设置小数点后的位数和舍入模式
        return num.divide(den, 10, RoundingMode.HALF_UP); // 保留 10 位小数
    }


    /**
     * 将Object转换为BigDecimal
     * @param obj 待转换的对象（支持Number、String等可转换为数字的类型）
     * @return 转换后的BigDecimal
     * @throws IllegalArgumentException 若对象无法转换为BigDecimal
     */
    public static BigDecimal toBigDecimal(Object obj) {
        if (obj == null) {
            throw new IllegalArgumentException("对象不能为null");
        }

        if (obj instanceof BigDecimal) {
            return (BigDecimal) obj;
        } else if (obj instanceof Number) {
            // 处理数字类型（Integer、Double、Long等）
            return new BigDecimal(obj.toString());
        } else if (obj instanceof String) {
            // 处理字符串类型（需符合数字格式）
            try {
                return new BigDecimal((String) obj);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("字符串无法转换为BigDecimal: " + obj, e);
            }
        } else {
            throw new IllegalArgumentException("不支持的类型转换: " + obj.getClass().getName());
        }
    }

    /**
     * 周期
     */
    private class PERIOD {
        private LocalDate PERIOD_BEGIN_DATE;
        private LocalDate PERIOD_END_DATE;
        private BigDecimal RATE;

        public void setPeriodBeginDate(LocalDate periodBeginDate) {
            this.PERIOD_BEGIN_DATE = periodBeginDate;
        }

        public void setPeriodEndDate(LocalDate periodEndDate) {
            this.PERIOD_END_DATE = periodEndDate;
        }

        public void setRate(BigDecimal rate) {
            this.RATE = rate;
        }

        public LocalDate getPeriodBeginDate() {
            return PERIOD_BEGIN_DATE;
        }

        public LocalDate getPeriodEndDate() {
            return PERIOD_END_DATE;
        }

        public String getPeriodBeginDateStr() {
            return PERIOD_BEGIN_DATE.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }

        public String getPeriodMonthStr() {
            return PERIOD_BEGIN_DATE.format(DateTimeFormatter.ofPattern("yyyyMM"));
        }

        public String getPeriodEndDateStr() {
            return PERIOD_END_DATE.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        }

        public BigDecimal getRate() {
            return RATE;
        }

    }
}
