package com.cf.imes.module.system.service.pay;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.id.core.util.SnowflakeIdWorker3rd;
import com.cf.imes.framework.pay.config.ChenfengPayConfig;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.funds.products.vo.product.ProductProcessorRespVO;
import com.cf.imes.module.system.dal.dataobject.funds.organamount.OrganAmountDO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDO;
import com.cf.imes.module.system.dal.dataobject.funds.products.ProductsDetailDO;
import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO;
import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO;
import com.cf.imes.module.system.dal.dataobject.pay.PayOrderDO;
import com.cf.imes.module.system.enums.pay.IncomeExpenseTypeEnum;
import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum;
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
import com.cf.imes.module.system.enums.pay.PayConstants;
import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum;
import com.cf.imes.module.system.enums.pay.PayProductChannelCodeEnum;
import com.cf.imes.module.system.enums.pay.TradeTypeEnum;
import com.cf.imes.module.system.enums.pay.DurationUnitEnum;
import com.cf.imes.module.system.framework.snowflake.config.MybatisIdProperties;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;

import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_AVAILABLE_BALANCE_INSUFFICIENT;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_BALANCE_INSUFFICIENT;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT;

/**
 * 产品购买类型处理器
 *
 * @author Gqr
 * @since 2025/8/14 10:54
 */
public class PayProductProcessor {

    private ProductsDO productsDO;

    private ProductsDetailDO productsDetailDO;

    private OrganAmountDO organAmountDO;

    private Long organId;

    private SnowflakeIdWorker3rd snowflakeIdWorker3rd;

    private ChenfengPayConfig payConfig;

    private MybatisIdProperties mybatisIdProperties;

    public PayProductProcessor(ProductsDO productsDO, ProductsDetailDO productsDetailDO, OrganAmountDO organAmountDO,
                               Long organId, SnowflakeIdWorker3rd snowflakeIdWorker3rd, ChenfengPayConfig payConfig,
                               MybatisIdProperties mybatisIdProperties) {
        this.productsDO = productsDO;
        this.productsDetailDO = productsDetailDO;
        this.organAmountDO = organAmountDO;
        this.organId = organId;
        this.snowflakeIdWorker3rd = snowflakeIdWorker3rd;
        this.payConfig = payConfig;
        this.mybatisIdProperties = mybatisIdProperties;
    }

    /**
     * 根据产品支付类型解析出对应的信息，
     *
     * @param payType
     */
    public ProductProcessorRespVO processPayment(PayProductChannelCodeEnum payType) {
        // 需要支付的价格
        BigDecimal price = productsDetailDO.getCurrentPrice();
        // 当前组织的余额
        BigDecimal balance = organAmountDO.getAmount();
        // 当前组织的赠送金
        BigDecimal gift = organAmountDO.getGiftAmount();

        switch (payType) {
            case BALANCE_ONLY:
                return payByBalance(price, balance);
            case GIFT_ONLY:
                return payByGift(price, gift);
            case ALIPAY_PC:
                return payByAlipay(price);
            case WECHAT_NATIVE:
                return payByWechat(price);
            case BALANCE_AND_GIFT:
                return payByBalanceAndGift(price, gift, balance);
            case BALANCE_AND_ALIPAY:
                return payByBalanceAndExternal(price, balance, PayChannelCodeEnum.ALIPAY_PC);
            case BALANCE_AND_WECHAT:
                return payByBalanceAndExternal(price, balance, PayChannelCodeEnum.WECHAT_NATIVE);
            case GIFT_AND_ALIPAY:
                return payByGiftAndExternal(price, gift, PayChannelCodeEnum.ALIPAY_PC);
            case GIFT_AND_WECHAT:
                return payByGiftAndExternal(price, gift, PayChannelCodeEnum.WECHAT_NATIVE);
            case BALANCE_AND_GIFT_AND_ALIPAY:
                return payByBalanceAndGiftAndExternal(price, gift, balance, PayChannelCodeEnum.ALIPAY_PC);
            case BALANCE_AND_GIFT_AND_WECHAT:
                return payByBalanceAndGiftAndExternal(price, gift, balance, PayChannelCodeEnum.WECHAT_NATIVE);
            default:
                return null;
        }
    }

    /**
     * 使用现金余额支付
     *
     * @param price
     * @param balance
     */
    private ProductProcessorRespVO payByBalance(BigDecimal price, BigDecimal balance) {
        if (balance.compareTo(price) < 0) {
            throw new ServiceException(PAY_PRODUCT_BALANCE_INSUFFICIENT);
        }
        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
                .totalAmount(price)
                .accountBalanceSpent(price)
                .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));
        // 新增收支明细
        // 现金余额减去购买金额
        BigDecimal newBalance = balance.subtract(price);
        BigDecimal giftAmount = organAmountDO.getGiftAmount();
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .cashAmountChange(price)
                .availableAmount(newBalance.add(giftAmount))
                .amount(newBalance)
                .giftAmount(giftAmount)
                .build();

        // 更新余额
        organAmountDO.setAmount(newBalance);
        organAmountDO.setGiftAmount(giftAmount);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO);
    }

    /**
     * 使用赠送金余额支付
     *
     * @param price
     * @param gift
     */
    private ProductProcessorRespVO payByGift(BigDecimal price, BigDecimal gift) {
        if (gift.compareTo(price) < 0) {
            throw new ServiceException(PAY_PRODUCT_GIFT_BALANCE_INSUFFICIENT);
        }
        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
                .totalAmount(price)
                .accountBalanceSpent(price)
                .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));
        // 新增收支明细
        // 赠送金余额减去购买金额
        BigDecimal newGift = gift.subtract(price);
        BigDecimal amount = organAmountDO.getAmount();
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .giftAmountChange(price)
                .availableAmount(amount.add(newGift))
                .amount(amount)
                .giftAmount(newGift)
                .build();

        // 更新余额
        organAmountDO.setAmount(amount);
        organAmountDO.setGiftAmount(newGift);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO);
    }

    /**
     * 使用支付宝支付
     *
     * @param price
     * @return
     */
    private ProductProcessorRespVO payByAlipay(BigDecimal price) {
        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
                .totalAmount(price)
                .alipaySpent(price)
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));

        // 新增第三方支付订单
        PayOrderDO payOrderDO = goExternalPay(price, PayChannelCodeEnum.ALIPAY_PC);
        return new ProductProcessorRespVO(purchaseRecordDO, null, payOrderDO, null);
    }

    /**
     * 使用微信支付
     *
     * @param price
     * @return
     */
    private ProductProcessorRespVO payByWechat(BigDecimal price) {
        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_ONLY.getCode())
                .totalAmount(price)
                .wechatSpent(price)
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));

        // 新增第三方支付订单
        PayOrderDO payOrderDO = goExternalPay(price, PayChannelCodeEnum.WECHAT_NATIVE);
        return new ProductProcessorRespVO(purchaseRecordDO, null, payOrderDO, null);
    }

    /**
     * 使用现在余额+赠送金余额支付
     *
     * @param price
     * @param gift
     * @param balance
     */
    private ProductProcessorRespVO payByBalanceAndGift(BigDecimal price, BigDecimal gift, BigDecimal balance) {
        BigDecimal remaining = price;
        BigDecimal usedGift = BigDecimal.ZERO;
        BigDecimal usedBalance = BigDecimal.ZERO;

        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode())
                .totalAmount(price)
                .isInvocing(InvoiceStatusEnum.INVOICABLE.getCode())
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));

        // 先用赠送金
        if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
            usedGift = remaining.min(gift);
            remaining = remaining.subtract(usedGift);
            purchaseRecordDO.setGiftMoneySpent(usedGift);
        }

        // 再用余额
        if (remaining.compareTo(BigDecimal.ZERO) > 0 && balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
            usedBalance = remaining.min(balance);
            remaining = remaining.subtract(usedBalance);
            purchaseRecordDO.setAccountBalanceSpent(usedBalance);
        }

        // 剩下走第三方
        if (remaining.compareTo(BigDecimal.ZERO) > 0) {
            throw new ServiceException(PAY_PRODUCT_AVAILABLE_BALANCE_INSUFFICIENT);
        }

        // 新增收支明细
        // 减去用掉的得到剩下的
        BigDecimal newAmounta = balance.subtract(usedBalance);
        BigDecimal newGift = gift.subtract(usedGift);
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .cashAmountChange(usedBalance)
                .giftAmountChange(usedGift)
                .availableAmount(newAmounta.add(newGift))
                .amount(newAmounta)
                .giftAmount(newGift)
                .build();

        // 更新余额
        organAmountDO.setAmount(newAmounta);
        organAmountDO.setGiftAmount(newGift);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, null, organAmountDO);
    }

    /**
     * 使用现金余额+第三方
     *
     * @param price
     * @param balance
     */
    private ProductProcessorRespVO payByBalanceAndExternal(BigDecimal price, BigDecimal balance, PayChannelCodeEnum payChannelCodeEnum) {
        BigDecimal remaining = price;
        BigDecimal usedBalance = BigDecimal.ZERO;

        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(payChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.BALANCE_AND_GIFT_AND_WECHAT.getCode())
                .totalAmount(price)
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));

        // 先用余额
        if (balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
            usedBalance = remaining.min(balance);
            remaining = remaining.subtract(usedBalance);
            purchaseRecordDO.setAccountBalanceSpent(usedBalance);
        }

        // 新增收支明细
        // 减去用掉的得到剩下的
        BigDecimal newAmounta = balance.subtract(usedBalance);
        BigDecimal giftAmount = organAmountDO.getGiftAmount();
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .cashAmountChange(usedBalance)
                .availableAmount(newAmounta.add(giftAmount))
                .amount(newAmounta)
                .giftAmount(giftAmount)
                .build();

        // 剩下走第三方
        PayOrderDO payOrderDO = null;
        if (remaining.compareTo(BigDecimal.ZERO) > 0) {
            payOrderDO = goExternalPay(remaining, payChannelCodeEnum);
            // 记录第三方支付支出
            if (PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum)) {
                purchaseRecordDO.setAlipaySpent(remaining);
            } else {
                purchaseRecordDO.setWechatSpent(remaining);
            }
        }

        // 更新余额
        organAmountDO.setAmount(newAmounta);
        organAmountDO.setGiftAmount(giftAmount);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO);
    }

    /**
     * 使用赠送金余额+第三方
     *
     * @param price
     * @param gift
     */
    private ProductProcessorRespVO payByGiftAndExternal(BigDecimal price, BigDecimal gift, PayChannelCodeEnum payChannelCodeEnum) {
        BigDecimal remaining = price;
        BigDecimal usedGift = BigDecimal.ZERO;

        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(payChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum) ? PayProductChannelCodeEnum.GIFT_AND_ALIPAY.getCode() : PayProductChannelCodeEnum.GIFT_AND_WECHAT.getCode())
                .totalAmount(price)
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));


        // 先用余额
        if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
            usedGift = remaining.min(gift);
            remaining = remaining.subtract(usedGift);
            purchaseRecordDO.setGiftMoneySpent(usedGift);
        }

        // 新增收支明细
        // 减去用掉的得到剩下的
        BigDecimal newGift = gift.subtract(usedGift);
        BigDecimal amount = organAmountDO.getAmount();
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .giftAmountChange(usedGift)
                .availableAmount(amount.add(newGift))
                .amount(amount)
                .giftAmount(newGift)
                .build();

        // 剩下走第三方
        PayOrderDO payOrderDO = null;
        if (remaining.compareTo(BigDecimal.ZERO) > 0) {
            goExternalPay(remaining, payChannelCodeEnum);
            // 记录第三方支付支出
            if (PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum)) {
                purchaseRecordDO.setAlipaySpent(remaining);
            } else {
                purchaseRecordDO.setWechatSpent(remaining);
            }
        }

        // 更新余额
        organAmountDO.setAmount(amount);
        organAmountDO.setGiftAmount(newGift);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO);
    }

    /**
     * 使用现在余额+赠送金余额支付
     *
     * @param price
     * @param gift
     * @param balance
     */
    private ProductProcessorRespVO payByBalanceAndGiftAndExternal(BigDecimal price, BigDecimal gift, BigDecimal balance, PayChannelCodeEnum payChannelCodeEnum) {
        BigDecimal remaining = price;
        BigDecimal usedGift = BigDecimal.ZERO;
        BigDecimal usedBalance = BigDecimal.ZERO;

        // 新增购买记录
        PurchaseRecordDO purchaseRecordDO = PurchaseRecordDO.builder()
                .id(IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId())
                .organId(organId)
                .productId(productsDO.getId())
                .productName(productsDO.getProductName())
                .productDetailsId(productsDetailDO.getId())
                .purchaseDuration(productsDetailDO.getDuration())
                .purchaseDurationUnit(productsDetailDO.getDurationUnit())
                .giftDuration(productsDetailDO.getGiftDuration())
                .giftDurationUnit(productsDetailDO.getGiftDurationUnit())
                .startTime(LocalDateTime.now())
                .paymentMethod(PayProductChannelCodeEnum.BALANCE_AND_GIFT.getCode())
                .totalAmount(price)
                .build();
        purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(),
                purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(),
                purchaseRecordDO.getGiftDuration(), purchaseRecordDO.getGiftDurationUnit()));

        // 先用赠送金
        if (gift != null && gift.compareTo(BigDecimal.ZERO) > 0) {
            usedGift = remaining.min(gift);
            remaining = remaining.subtract(usedGift);
            purchaseRecordDO.setGiftMoneySpent(usedGift);
        }

        // 再用余额
        if (remaining.compareTo(BigDecimal.ZERO) > 0 && balance != null && balance.compareTo(BigDecimal.ZERO) > 0) {
            usedBalance = remaining.min(balance);
            remaining = remaining.subtract(usedBalance);
            purchaseRecordDO.setAccountBalanceSpent(usedBalance);
        }

        // 新增收支明细
        // 减去用掉的得到剩下的
        BigDecimal newAmounta = balance.subtract(usedBalance);
        BigDecimal newGift = gift.subtract(usedGift);
        IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder()
                .organId(organId)
                .purchaseId(purchaseRecordDO.getId())
                .tradeType(TradeTypeEnum.PRODUCT.getCode())
                .incomeExpenseType(IncomeExpenseTypeEnum.EXPENSES.getCode())
                .cashAmountChange(usedBalance)
                .giftAmountChange(usedGift)
                .availableAmount(newAmounta.add(newGift))
                .amount(newAmounta)
                .giftAmount(newGift)
                .build();

        // 剩下走第三方
        PayOrderDO payOrderDO = null;
        if (remaining.compareTo(BigDecimal.ZERO) > 0) {
            payOrderDO = goExternalPay(remaining, payChannelCodeEnum);
            // 记录第三方支付支出
            if (PayChannelCodeEnum.ALIPAY_PC.equals(payChannelCodeEnum)) {
                purchaseRecordDO.setAlipaySpent(remaining);
            } else {
                purchaseRecordDO.setWechatSpent(remaining);
            }
        }

        // 更新余额
        organAmountDO.setAmount(newAmounta);
        organAmountDO.setGiftAmount(newGift);
        return new ProductProcessorRespVO(purchaseRecordDO, incomeExpenseDetailsDO, payOrderDO, organAmountDO);
    }

    /**
     * 外部支付
     *
     * @param price
     * @param payChannelCodeEnum
     * @return
     */
    private PayOrderDO goExternalPay(BigDecimal price, PayChannelCodeEnum payChannelCodeEnum) {
        PayOrderDO payOrderDO = new PayOrderDO();

        Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId();
        Long organId = SecurityFrameworkUtils.getUserOrganId();

        payOrderDO.setId(payOrderId);
        payOrderDO.setPayNo(String.format(PayConstants.PAY_ORDER_PREFIX_FORMAT, organId, payOrderId, DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN)));
        payOrderDO.setStatus(PayOrderStatusEnum.WAITING);
        payOrderDO.setExpireTime(LocalDateTime.now().plus(payConfig.getPayExpireTime()));
        payOrderDO.setPrice(price);
        payOrderDO.setSubject(productsDO.getProductName());
        payOrderDO.setChannelCode(payChannelCodeEnum);
        payOrderDO.setOrganId(organId);
        return payOrderDO;
    }

    /**
     * 计算结束时间
     *
     * @param startTime
     * @param duration
     * @param durationUnit
     * @param giftDuration
     * @param giftDurationUnit
     * @return
     */
    private static LocalDateTime calculateEndTime(LocalDateTime startTime, String duration, Integer durationUnit,
                                                  String giftDuration, Integer giftDurationUnit) {

        // 加正常时间
        if (StringUtils.isNotEmpty(duration) && durationUnit != null) {
            startTime = addTime(startTime, Integer.parseInt(duration), durationUnit);
        }

        // 加赠送时间
        if (StringUtils.isNotEmpty(giftDuration) && giftDurationUnit != null) {
            startTime = addTime(startTime, Integer.parseInt(giftDuration), giftDurationUnit);
        }

        return startTime;
    }

    /**
     * 基于时长和单位累加time
     *
     * @param time
     * @param duration
     * @param unit
     * @return
     */
    private static LocalDateTime addTime(LocalDateTime time, int duration, int unit) {
        switch (DurationUnitEnum.fromStatus(unit)) {
            case YEAR:
                return time.plusYears(duration);
            case MONTH:
                return time.plusMonths(duration);
            case DAY:
                return time.plusDays(duration);
            default:
                return time;
        }
    }

}
