package com.jeesharp.business.modules.mall.application.support;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.jeesharp.business.modules.common.constants.PlatformTenantConstants;
import com.jeesharp.business.modules.mall.entity.MallOrder;
import com.jeesharp.business.modules.mall.entity.MallPayBatch;
import com.jeesharp.business.modules.mall.enums.MallOrderPaymentPhaseEnum;
import com.jeesharp.business.modules.mall.enums.MallPayBatchStatusEnum;
import com.jeesharp.business.modules.mall.vo.MallPayPrepareVO;
import com.jeesharp.business.modules.mall.vo.MallPayStatusVO;
import com.jeesharp.business.modules.pay.support.VerticalPayPrepareSupport;
import com.jeesharp.framework.exception.BizErrorException;
import lombok.AllArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;

/**
 * 商城订单预支付 VO 组装（Application 层）。
 *
 * @author jeesharp
 * @since 2026-07-03
 */
@Component
@AllArgsConstructor
public class MallOrderPayAssembler {

    private static final String ATTACH_BIZ_TYPE = "mall";
    private static final String PROFILE_PRO = "pro";
    private static final long PREPAY_AMOUNT_FEN_NON_PRO = 1L;
    private static final int MALL_PAY_SIGN_LENGTH = 32;
    private static final String PAY_AMOUNT_INVALID_MESSAGE = "支付金额必须大于0";

    private final Environment environment;

    public MallPayPrepareVO buildSingleOrderPrepareVo(MallOrder order, String payChannel, String payOrderId) {
        String tradeNo = StrUtil.blankToDefault(order.getOrderNo(), order.getId());
        long amountFen = resolveAmountFenForOrder(order);
        MallPayPrepareVO vo = new MallPayPrepareVO();
        VerticalPayPrepareSupport.fillPrepareVo(
                vo,
                ATTACH_BIZ_TYPE,
                order.getId(),
                tradeNo,
                payOrderId,
                payChannel,
                MallOrderPaymentPhaseEnum.PREPARING.getCode(),
                "",
                order.getTenantId(),
                buildSinglePayDescription(order),
                amountFen);
        vo.setAttach(buildMallAttach(order.getId(), tradeNo, payOrderId, order.getMemberId(), null));
        VerticalPayPrepareSupport.fillMockPrepayPackage(
                vo,
                VerticalPayPrepareSupport.DEFAULT_MOCK_WECHAT_APP_ID,
                VerticalPayPrepareSupport.DEFAULT_MOCK_PREPAY_ID_PREFIX,
                payOrderId,
                MALL_PAY_SIGN_LENGTH);
        return vo;
    }

    public MallPayPrepareVO buildBatchPrepareVo(
            MallPayBatch batch, List<MallOrder> subOrders, String payChannel, String payOrderId) {
        String tradeNo = StrUtil.blankToDefault(batch.getBatchNo(), batch.getId());
        int subOrderCount = subOrders == null ? 0 : subOrders.size();
        long amountFen = resolveAmountFen(batch.getPayAmount());
        MallPayPrepareVO vo = new MallPayPrepareVO();
        VerticalPayPrepareSupport.fillPrepareVo(
                vo,
                ATTACH_BIZ_TYPE,
                batch.getId(),
                tradeNo,
                payOrderId,
                payChannel,
                MallOrderPaymentPhaseEnum.PREPARING.getCode(),
                "",
                PlatformTenantConstants.PLATFORM_TENANT_ID,
                buildBatchPayDescription(batch, subOrderCount),
                amountFen);
        vo.setBatchId(batch.getId());
        vo.setOrderId(batch.getId());
        vo.setAttach(buildMallAttach(batch.getId(), tradeNo, payOrderId, batch.getMemberId(), batch.getId()));
        VerticalPayPrepareSupport.fillMockPrepayPackage(
                vo,
                VerticalPayPrepareSupport.DEFAULT_MOCK_WECHAT_APP_ID,
                VerticalPayPrepareSupport.DEFAULT_MOCK_PREPAY_ID_PREFIX,
                payOrderId,
                MALL_PAY_SIGN_LENGTH);
        return vo;
    }

    public MallPayStatusVO buildPayStatusVo(MallOrder entity) {
        MallPayStatusVO vo = new MallPayStatusVO();
        vo.setOrderId(entity.getId());
        vo.setTradeOrderId(StrUtil.blankToDefault(entity.getOrderNo(), entity.getId()));
        vo.setPayOrderId("");
        vo.setPayChannel(StrUtil.blankToDefault(entity.getPayType(), ""));
        boolean paid = entity.getPayTime() != null;
        vo.setPayStatus(paid ? MallOrderPaymentPhaseEnum.PAID.getCode() : MallOrderPaymentPhaseEnum.UNPAID.getCode());
        vo.setOrderStatus(entity.getStatus());
        vo.setExpireTime("");
        return vo;
    }

    public MallPayStatusVO buildBatchPayStatusVo(MallPayBatch batch) {
        MallPayStatusVO vo = new MallPayStatusVO();
        vo.setBatchId(batch.getId());
        vo.setOrderId(batch.getId());
        vo.setTradeOrderId(StrUtil.blankToDefault(batch.getBatchNo(), batch.getId()));
        vo.setPayOrderId("");
        vo.setPayChannel(StrUtil.blankToDefault(batch.getPayType(), ""));
        boolean paid = MallPayBatchStatusEnum.PAID.getCode().equals(batch.getStatus()) || batch.getPayTime() != null;
        vo.setPayStatus(paid ? MallOrderPaymentPhaseEnum.PAID.getCode() : MallOrderPaymentPhaseEnum.UNPAID.getCode());
        vo.setOrderStatus(batch.getStatus());
        vo.setExpireTime("");
        return vo;
    }

    public String newPayOrderId() {
        return VerticalPayPrepareSupport.newPayOrderId();
    }

    public long resolveAmountFenForOrder(MallOrder order) {
        if (environment.matchesProfiles(PROFILE_PRO)) {
            long amountFen = VerticalPayPrepareSupport.yuanToFen(resolvePayAmount(order));
            if (amountFen <= 0) {
                throw new BizErrorException(PAY_AMOUNT_INVALID_MESSAGE);
            }
            return amountFen;
        }
        return PREPAY_AMOUNT_FEN_NON_PRO;
    }

    public long resolveAmountFen(BigDecimal payAmount) {
        if (environment.matchesProfiles(PROFILE_PRO)) {
            long amountFen = VerticalPayPrepareSupport.yuanToFen(payAmount);
            if (amountFen <= 0) {
                throw new BizErrorException(PAY_AMOUNT_INVALID_MESSAGE);
            }
            return amountFen;
        }
        return PREPAY_AMOUNT_FEN_NON_PRO;
    }

    private static String buildMallAttach(
            String orderId, String tradeOrderId, String payOrderId, String memberId, String payBatchId) {
        var obj = JSONUtil.createObj()
                .set("bizType", ATTACH_BIZ_TYPE)
                .set("orderId", orderId)
                .set("tradeOrderId", tradeOrderId)
                .set("payOrderId", payOrderId)
                .set("memberId", StrUtil.blankToDefault(memberId, ""));
        if (StrUtil.isNotBlank(payBatchId)) {
            obj.set("payBatchId", payBatchId);
        }
        return obj.toString();
    }

    private static String buildSinglePayDescription(MallOrder order) {
        String no = StrUtil.blankToDefault(order.getOrderNo(), order.getId());
        return "商城订单-" + no;
    }

    private static String buildBatchPayDescription(MallPayBatch batch, int subOrderCount) {
        String batchNo = StrUtil.blankToDefault(batch.getBatchNo(), batch.getId());
        if (subOrderCount <= 1) {
            return "商城订单-" + batchNo;
        }
        return "商城合并支付-" + batchNo + "(" + subOrderCount + "笔)";
    }

    private static BigDecimal resolvePayAmount(MallOrder order) {
        if (order.getPayAmount() != null && order.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
            return order.getPayAmount();
        }
        return order.getTotalAmount() == null ? BigDecimal.ZERO : order.getTotalAmount();
    }
}
