package com.jeesharp.business.modules.pay.service.weixin;

import cn.hutool.core.util.StrUtil;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatCredentialSupport;
import com.jeesharp.business.modules.pay.config.weixin.PayWechatWxPayClientHolder;
import com.jeesharp.business.modules.pay.dto.BasicPayOrderData;
import com.jeesharp.business.modules.pay.support.WechatPayAttachSupport;
import com.jeesharp.business.modules.pay.paywechatconfig.entity.PayWechatConfig;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

/**
 * 支付中心：微信 V3 统一下单（JSAPI / APP）。
 *
 * @author jeesharp
 * @since 2026-05-25
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PayWechatV3UnifiedOrderService {

    private final PayWechatWxPayClientHolder payWechatWxPayClientHolder;

    public Object createOrder(BasicPayOrderData payOrderData) {
        String tenantId = payOrderData.getTenantId();
        PayWechatConfig config = payWechatWxPayClientHolder.requireActiveConfig(tenantId);
        if (!PayWechatCredentialSupport.isWxEnabled(config)) {
            throw new IllegalStateException("未启用真实微信 V3 下单：请在管理后台【支付 · 微信配置】开启");
        }
        validateMerchantConfig(config);
        WxPayService wxPayService = payWechatWxPayClientHolder.requireWxPayService(tenantId);

        TradeTypeEnum tradeType = resolveTradeType(payOrderData.getWxTradeType());
        WxPayUnifiedOrderV3Request v3 = buildV3Request(payOrderData, tradeType, config);

        try {
            if (tradeType == TradeTypeEnum.JSAPI) {
                return wxPayService.createOrderV3(TradeTypeEnum.JSAPI, v3);
            }
            return wxPayService.createOrderV3(TradeTypeEnum.APP, v3);
        } catch (WxPayException e) {
            String msg = e.getCustomErrorMsg() != null && !e.getCustomErrorMsg().isBlank()
                ? e.getCustomErrorMsg() : e.getMessage();
            log.error("Pay center wechat V3 unified order failed: {}", msg, e);
            throw new IllegalStateException("微信支付下单失败: " + msg, e);
        }
    }

    public String resolveNotifyUrl(BasicPayOrderData payOrderData) {
        if (StringUtils.hasText(payOrderData.getNotifyUrl())) {
            return payOrderData.getNotifyUrl().trim();
        }
        PayWechatConfig config = payWechatWxPayClientHolder.requireActiveConfig(payOrderData.getTenantId());
        String base = StrUtil.trimToNull(config.getNotifyBaseUrl());
        String path = StrUtil.blankToDefault(config.getPayCenterNotifyPath(), "/api/pay/payNotify/wxNotify");
        if (base != null) {
            if (path.startsWith("/")) {
                return base.replaceAll("/+$", "") + path;
            }
            return base.replaceAll("/+$", "") + "/" + path;
        }
        throw new IllegalStateException("未配置 notifyUrl，且后台微信支付配置 notifyBaseUrl 为空");
    }

    private WxPayUnifiedOrderV3Request buildV3Request(BasicPayOrderData payOrderData, TradeTypeEnum tradeType,
                                                      PayWechatConfig config) {
        String outTradeNo = StrUtil.blankToDefault(payOrderData.getOrderNumber(), null);
        if (outTradeNo == null) {
            throw new IllegalArgumentException("orderNumber 不能为空（商户单号）");
        }
        String description = StrUtil.blankToDefault(payOrderData.getSubject(), payOrderData.getDescription());
        if (StrUtil.isBlank(description)) {
            throw new IllegalArgumentException("subject 或 description 不能为空");
        }

        WxPayUnifiedOrderV3Request v3 = new WxPayUnifiedOrderV3Request();
        v3.setDescription(description);
        v3.setOutTradeNo(outTradeNo);
        String attach = StrUtil.blankToDefault(payOrderData.getAttach(),
            StrUtil.blankToDefault(payOrderData.getUserId(), ""));
        v3.setAttach(WechatPayAttachSupport.ensureWithinLimit(attach));
        v3.setNotifyUrl(resolveNotifyUrl(payOrderData));

        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        amount.setTotal(payOrderData.getPayAmount().intValue());
        amount.setCurrency("CNY");
        v3.setAmount(amount);

        if (tradeType == TradeTypeEnum.JSAPI) {
            if (StrUtil.isBlank(payOrderData.getPayerOpenId())) {
                throw new IllegalArgumentException("JSAPI 下单必须提供 payerOpenId");
            }
            WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
            payer.setOpenid(payOrderData.getPayerOpenId().trim());
            v3.setPayer(payer);
        }
        if (Boolean.TRUE.equals(payOrderData.getNeedProfitSharing())) {
            WxPayUnifiedOrderV3Request.SettleInfo settleInfo = new WxPayUnifiedOrderV3Request.SettleInfo();
            settleInfo.setProfitSharing(true);
            v3.setSettleInfo(settleInfo);
        }
        return v3;
    }

    private static TradeTypeEnum resolveTradeType(String wxTradeType) {
        if (wxTradeType == null || wxTradeType.isBlank() || "JSAPI".equalsIgnoreCase(wxTradeType.trim())) {
            return TradeTypeEnum.JSAPI;
        }
        if ("APP".equalsIgnoreCase(wxTradeType.trim())) {
            return TradeTypeEnum.APP;
        }
        throw new IllegalArgumentException("不支持的 wxTradeType: " + wxTradeType + "，仅支持 JSAPI、APP");
    }

    private void validateMerchantConfig(PayWechatConfig config) {
        require(config.getAppId(), "未配置 AppId");
        require(config.getMchId(), "未配置商户号 mchId");
        require(PayWechatCredentialSupport.resolveMerchantSerialNo(config),
            "未配置 merchantSerialNo，且无法从 certPrivateCertPath 解析");
        require(PayWechatCredentialSupport.resolveMerchantPrivateKeyPem(config),
            "未配置 merchantPrivateKeyPem，且无法从 certPrivateKeyPath 读取");
    }

    private static void require(String value, String message) {
        if (value == null || value.isBlank()) {
            throw new IllegalStateException(message);
        }
    }
}
