package org.dromara.main.config;

import cn.hutool.crypto.PemUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.refund.RefundService;
import jakarta.annotation.PostConstruct;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.main.domain.PaymentConfig;
import org.dromara.main.service.IPaymentConfigService;
import org.dromara.main.utils.WXPayUtility;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URL;
import java.security.PrivateKey;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 微信支付配置类
 */
@Slf4j
@Component
@Data
@ConfigurationProperties(prefix = "wxpay")
public class WxPayConfig {

    private String appId;
    private String mchId;
    private String apiV3Key;
    private String privateKeyPath;
    private String certSerialNo;
    private String notifyUrl;

    private JsapiService jsapiService;
    private RefundService refundService;
    private PrivateKey privateKey;

    @Autowired
    private ResourceLoader resourceLoader;

    @Lazy
    @Autowired(required = false)
    private IPaymentConfigService paymentConfigService;

    @PostConstruct
    public void init() {
        try {
            log.info("开始初始化微信支付配置...");

            // 1. 尝试从数据库获取配置
            if (paymentConfigService != null) {
                try {
                    PaymentConfig dbConfig = paymentConfigService.getEnabledWechatConfig();
                    if (dbConfig != null) {
                        log.info("检测到数据库微信支付配置，尝试覆盖本地配置...");
                        if (StringUtils.isNotBlank(dbConfig.getAppId())) {
                            this.appId = dbConfig.getAppId();
                        }
                        if (StringUtils.isNotBlank(dbConfig.getMchId())) {
                            this.mchId = dbConfig.getMchId();
                        }
                        if (StringUtils.isNotBlank(dbConfig.getApiV3Key())) {
                            this.apiV3Key = dbConfig.getApiV3Key();
                        }
                        if (StringUtils.isNotBlank(dbConfig.getPrivateKeyPath())) {
                            this.privateKeyPath = dbConfig.getPrivateKeyPath();
                        }
                        if (StringUtils.isNotBlank(dbConfig.getSerialNo())) {
                            this.certSerialNo = dbConfig.getSerialNo();
                        }
                        if (StringUtils.isNotBlank(dbConfig.getPayNotifyUrl())) {
                            this.notifyUrl = dbConfig.getPayNotifyUrl();
                        }
                    }
                } catch (Exception e) {
                    log.warn("从数据库获取微信支付配置失败，将使用配置文件：{}", e.getMessage());
                }
            }

            // 检查配置是否完整
            if (StringUtils.isEmpty(privateKeyPath)) {
                log.warn("微信支付配置不完整 (privateKeyPath 为空)，跳过初始化");
                return;
            }

            // 加载商户私钥
            InputStream privateKeyStream = null;
            if (privateKeyPath.startsWith("http")) {
                // 如果是网络路径 (OSS)
                privateKeyStream = new URL(privateKeyPath).openStream();
                log.info("从网络加载商户私钥成功");
            } else {
                // 如果是本地路径或 classpath
                Resource resource = resourceLoader.getResource(privateKeyPath);
                if (resource.exists()) {
                    privateKeyStream = resource.getInputStream();
                    log.info("从本地/资源路径加载商户私钥成功: {}", privateKeyPath);
                }
            }

            if (privateKeyStream != null) {
                privateKey = PemUtil.readPemPrivateKey(privateKeyStream);
                privateKeyStream.close();
            } else {
                log.warn("找不到商户私钥：{}，跳过微信支付初始化", privateKeyPath);
                return;
            }

            // 配置微信支付
            Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(mchId)
                .privateKey(privateKey)
                .merchantSerialNumber(certSerialNo)
                .apiV3Key(apiV3Key)
                .build();

            // 初始化服务
            jsapiService = new JsapiService.Builder().config(config).build();
            refundService = new RefundService.Builder().config(config).build();

            log.info("微信支付配置初始化成功");
        } catch (Exception e) {
            log.error("初始化微信支付配置失败", e);
            // 不抛异常，允许应用启动
            log.warn("微信支付功能将不可用");
        }
    }

    /**
     * 生成 JSAPI 支付参数
     */
    public Map<String, String> createJsapiPayParams(String openid, BigDecimal payPrice, String remark, String orderCode) {
        if (jsapiService == null) {
            throw new RuntimeException("微信支付服务未初始化，请检查配置");
        }

        try {
            // 构建预支付请求
            PrepayRequest request = new PrepayRequest();
            request.setAppid(appId);
            request.setMchid(mchId);
            request.setDescription(StringUtils.isEmpty(remark) ? "订单支付" : remark);
            request.setOutTradeNo(orderCode);
            request.setNotifyUrl(notifyUrl);

            // 金额设置（转换为分）
            Amount amount = new Amount();
            int totalCents = payPrice.multiply(new BigDecimal(100)).intValue();
            log.info("微信支付预下单 - 金额转换: 元={}, 分={}", payPrice, totalCents);
            amount.setTotal(totalCents);
            request.setAmount(amount);

            // 支付者信息
            Payer payer = new Payer();
            payer.setOpenid(openid);
            request.setPayer(payer);

            // 调用预支付接口
            PrepayResponse response = jsapiService.prepay(request);

            // 生成前端支付参数
            return generatePaySign(response.getPrepayId());
        } catch (Exception e) {
            throw new RuntimeException("生成JSAPI支付参数失败", e);
        }
    }

    /**
     * 生成支付签名
     */
    private Map<String, String> generatePaySign(String prepayId) {
        try {
            String timeStamp = String.valueOf(Instant.now().getEpochSecond());
            String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
            String packageStr = "prepay_id=" + prepayId;

            // 构造签名串
            String signMessage = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + packageStr + "\n";

            // 使用商户私钥签名
            String paySign = WXPayUtility.sign(signMessage, "SHA256withRSA", privateKey);

            // 返回支付参数
            Map<String, String> payParams = new HashMap<>();
            payParams.put("appId", appId);
            payParams.put("timeStamp", timeStamp);
            payParams.put("nonceStr", nonceStr);
            payParams.put("package", packageStr);
            payParams.put("signType", "RSA");
            payParams.put("paySign", paySign);
            payParams.put("partnerId", mchId);
            payParams.put("prepayId", prepayId);
            return payParams;
        } catch (Exception e) {
            throw new RuntimeException("生成支付签名失败", e);
        }
    }

    public JsapiService getJsapiService() { return jsapiService; }
    public RefundService getRefundService() { return refundService; }
}
