package com.jeesharp.business.modules.pay.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * 各业态预支付 VO 组装公共片段（attach、金额分、JSAPI 占位字段等）。
 *
 * @author jeesharp
 * @since 2026-07-03
 */
public final class VerticalPayPrepareSupport {

    public static final String SIGN_TYPE_RSA = "RSA";

    /** 非生产/网关占位：微信 appId */
    public static final String DEFAULT_MOCK_WECHAT_APP_ID = "wx-demo-appid";

    /** 非生产/网关占位：prepay_id 前缀 */
    public static final String DEFAULT_MOCK_PREPAY_ID_PREFIX = "prepay_id=mock_";

    /** 年卡业态占位 appId（与存量 mock 一致） */
    public static final String YEARCARD_MOCK_WECHAT_APP_ID = "wx_mock_yearcard";

    /** 年卡业态占位 prepay_id 前缀 */
    public static final String YEARCARD_MOCK_PREPAY_ID_PREFIX = "prepay_id=";

    private static final int NONCE_STR_LENGTH = 16;

    public static final int DEFAULT_PAY_SIGN_LENGTH = 64;

    private VerticalPayPrepareSupport() {
    }

    public static String newPayOrderId() {
        return "PAY" + IdUtil.getSnowflakeNextIdStr();
    }

    public static String buildAttach(String bizType, String orderId, String tradeOrderId, String payOrderId) {
        return buildAttach(bizType, null, orderId, tradeOrderId, payOrderId);
    }

    public static String buildAttach(
            String bizType, String subType, String orderId, String tradeOrderId, String payOrderId) {
        var obj = JSONUtil.createObj()
                .set("bizType", bizType)
                .set("orderId", orderId)
                .set("tradeOrderId", tradeOrderId)
                .set("payOrderId", payOrderId);
        if (StrUtil.isNotBlank(subType)) {
            obj.set("subType", subType);
        }
        return obj.toString();
    }

    public static long yuanToFen(BigDecimal yuan) {
        if (yuan == null) {
            return 0L;
        }
        return yuan.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.HALF_UP).longValue();
    }

    public static String formatDateTime(LocalDateTime dateTime, DateTimeFormatter formatter) {
        if (dateTime == null || formatter == null) {
            return "";
        }
        return dateTime.format(formatter);
    }

    /**
     * 写入各业态 {@code *PayPrepareVO} 通用字段（字段名须与存量 VO 对齐）。
     */
    public static void fillPrepareVo(
            Object targetVo,
            String attachBizType,
            String orderId,
            String tradeOrderId,
            String payOrderId,
            String payChannel,
            String payStatus,
            String expireTime,
            String tenantId,
            String description,
            Long amountFen) {
        fillPrepareVo(
                targetVo,
                attachBizType,
                null,
                orderId,
                tradeOrderId,
                payOrderId,
                payChannel,
                payStatus,
                expireTime,
                tenantId,
                description,
                amountFen);
    }

    public static void fillPrepareVo(
            Object targetVo,
            String attachBizType,
            String attachSubType,
            String orderId,
            String tradeOrderId,
            String payOrderId,
            String payChannel,
            String payStatus,
            String expireTime,
            String tenantId,
            String description,
            Long amountFen) {
        if (targetVo == null) {
            return;
        }
        BeanUtil.setFieldValue(targetVo, "orderId", orderId);
        BeanUtil.setFieldValue(targetVo, "tradeOrderId", tradeOrderId);
        BeanUtil.setFieldValue(targetVo, "outTradeNo", tradeOrderId);
        BeanUtil.setFieldValue(
                targetVo, "attach", buildAttach(attachBizType, attachSubType, orderId, tradeOrderId, payOrderId));
        BeanUtil.setFieldValue(targetVo, "description", description);
        BeanUtil.setFieldValue(targetVo, "amountFen", amountFen);
        BeanUtil.setFieldValue(targetVo, "payOrderId", payOrderId);
        BeanUtil.setFieldValue(targetVo, "payChannel", payChannel);
        BeanUtil.setFieldValue(targetVo, "payStatus", payStatus);
        BeanUtil.setFieldValue(targetVo, "expireTime", StrUtil.nullToEmpty(expireTime));
        BeanUtil.setFieldValue(targetVo, "tenantId", tenantId);
        fillClientSignPlaceholders(targetVo);
    }

    /**
     * 微信 JSAPI 调起参数占位（网关 {@link com.jeesharp.business.pay.support.WechatJsapiPrepayVoMapper} 会覆盖）。
     */
    public static void fillClientSignPlaceholders(Object targetVo) {
        if (targetVo == null) {
            return;
        }
        BeanUtil.setFieldValue(targetVo, "signType", SIGN_TYPE_RSA);
        BeanUtil.setFieldValue(targetVo, "nonceStr", RandomUtil.randomString(NONCE_STR_LENGTH));
        BeanUtil.setFieldValue(targetVo, "timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
    }

    /**
     * 写入微信 JSAPI 调起占位字段（appId、package、paySign）；网关预支付成功后会覆盖。
     */
    public static void fillMockPrepayPackage(Object targetVo, String payOrderId) {
        fillMockPrepayPackage(
                targetVo, DEFAULT_MOCK_WECHAT_APP_ID, DEFAULT_MOCK_PREPAY_ID_PREFIX, payOrderId, DEFAULT_PAY_SIGN_LENGTH);
    }

    /**
     * 写入微信 JSAPI 调起占位字段，可指定 appId、prepay 前缀与 paySign 长度。
     */
    public static void fillMockPrepayPackage(
            Object targetVo, String appId, String prepayIdPrefix, String payOrderId, int paySignLength) {
        if (targetVo == null) {
            return;
        }
        BeanUtil.setFieldValue(targetVo, "appId", appId);
        if (StrUtil.isNotBlank(payOrderId)) {
            BeanUtil.setFieldValue(targetVo, "packageValue", prepayIdPrefix + payOrderId);
        }
        BeanUtil.setFieldValue(targetVo, "paySign", RandomUtil.randomStringUpper(paySignLength));
    }

    public static String joinDescription(String primary, String secondary) {
        String left = StrUtil.blankToDefault(primary, "");
        String right = StrUtil.blankToDefault(secondary, "");
        if (StrUtil.isBlank(left)) {
            return right;
        }
        return StrUtil.isBlank(right) ? left : left + "-" + right;
    }
}
