package cn.iocoder.yudao.module.system.controller.admin.pay;

import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.system.dal.dataobject.pay.WechatMiniOrderDO;
import cn.iocoder.yudao.module.system.dal.dataobject.pay.WechatMiniOrderItemDO;
import cn.iocoder.yudao.module.system.dal.dataobject.pay.WechatMiniPayRequestInfoDO;
import cn.iocoder.yudao.module.system.dal.mysql.pay.WechatMiniOrderItemMapper;
import cn.iocoder.yudao.module.system.dal.mysql.pay.WechatMiniOrderMapper;
import cn.iocoder.yudao.module.system.dal.mysql.pay.WechatMiniPayRequestInfoMapper;
import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.system.service.miniapp.MiniAppUserService;
import cn.iocoder.yudao.module.system.util.pay.CertUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RSAPublicKeyNotificationConfig;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.annotation.security.PermitAll;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exceptionCustomMsg;


/**
 * 微信小程序统一下单控制器
 */
@Slf4j
@RestController
@RequestMapping("/pay/wx")
public class WeChatPayController {

    @Autowired
    private WechatMiniPayRequestInfoMapper wechatMiniPayRequestInfoMapper;

    @Autowired
    private MiniAppUserService miniAppUserService;

    @Autowired
    private WechatMiniOrderMapper wechatMiniOrderMapper;

    @Autowired
    private WechatMiniOrderItemMapper wechatMiniOrderItemMapper;

    @Value("${wechat.miniapp.pay.public-key}")
    private String publicKey;

    @Value("${wechat.miniapp.pay.public-key-id}")
    private String publicKeyId;

    @Value("${wechat.miniapp.pay.api-v3-key}")
    private String apiV3Key;

    private static final String PREFIX = "OUT_TRADE_NO_";
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HHmmss");

    @PostMapping(value = "/unifiedOrder", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE})
    @PermitAll
    @TenantIgnore
    public CommonResult<JSONObject> unifiedOrder(@RequestBody WechatMiniOrderDO req) throws Exception {
        log.info("req:{}",req.toString());
        String openId = miniAppUserService.getByPhone(req.getPhone()).getOpenId();
        String outTradeNo = generateOrderNo();
        req.setOutTradeNo(outTradeNo);
        // 记录支付订单信息
        String description = insertMiniOrder(req);
        WechatMiniPayRequestInfoDO wechatMiniPayRequestInfoDO = wechatMiniPayRequestInfoMapper.selectOne(new LambdaQueryWrapperX<WechatMiniPayRequestInfoDO>()
                .eq(WechatMiniPayRequestInfoDO::getStatus, 1)
                .eq(WechatMiniPayRequestInfoDO::getTenantId, req.getTenantId()));
        if(wechatMiniPayRequestInfoDO == null){
            return CommonResult.error(201,"该机构没有微信支付相关信息，请先开通微信支付");
        }
        String merchantSerialNumber = CertUtils.getMerchantSerialNumberFromPem(wechatMiniPayRequestInfoDO.getCertPem());
        // 1. 构建配置类
        Config config =
                new RSAPublicKeyConfig.Builder()
                        .merchantId(wechatMiniPayRequestInfoDO.getSpMchid())
                        .privateKey(wechatMiniPayRequestInfoDO.getPrivateKey())          // 商户私钥字符串
                        .publicKey(publicKey)   // 微信支付公钥字符串
                        .publicKeyId(publicKeyId)  // 公钥ID（必须！）
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();

        // 2. 创建 JSAPI 扩展服务
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();

        // 3. 构造统一下单请求
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(req.getTotalAmount()); // 金额，单位：分
        request.setAmount(amount);
        request.setSpAppid(wechatMiniPayRequestInfoDO.getSpAppid());
        request.setSpMchid(wechatMiniPayRequestInfoDO.getSpMchid());
        request.setSubAppid(wechatMiniPayRequestInfoDO.getSubAppid());
        request.setSubMchid(wechatMiniPayRequestInfoDO.getSubMchid());
        request.setDescription(description);
        request.setNotifyUrl(wechatMiniPayRequestInfoDO.getNotifyUrl());
        request.setOutTradeNo(outTradeNo);
        JSONObject attach = new JSONObject();
        attach.put("outTradeNo",outTradeNo);
        request.setAttach(attach.toJSONString());

        Payer payer = new Payer();
        payer.setSpOpenid(openId); // 必须传入用户的openid
        request.setPayer(payer);
        String requestPaymentAppid = request.getSubAppid() != null ? request.getSubAppid() : request.getSpAppid();
        // 4. 调用扩展方法，直接得到前端调起支付所需的参数
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request,requestPaymentAppid);

        // 5. 输出或返回给前端
        JSONObject result = new JSONObject();
        result.put("package",response.getPackageVal());
        result.put("nonceStr",response.getNonceStr());
        result.put("timeStamp",response.getTimeStamp());
        result.put("signType",response.getSignType());
        result.put("paySign",response.getPaySign());
        result.put("outTradeNo",outTradeNo);
        return CommonResult.success(result);
    }
    @PostMapping(value = "/callback", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE, MediaType.APPLICATION_JSON_VALUE})
    @PermitAll
    @TenantIgnore
    public String weChatPayCallBack(HttpServletRequest request){
        log.info("微信支付回调");
        try {
            // 1. 读取请求体
            String requestBody = getRequestBody(request);
            // 2. 获取请求头
            String wechatPaySerial = request.getHeader("Wechatpay-Serial");
            String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
            String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
            String wechatSignature = request.getHeader("Wechatpay-Signature");
            // 3. 构建 RequestParam
            com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder()
                    .serialNumber(wechatPaySerial)
                    .nonce(wechatpayNonce)
                    .signature(wechatSignature)
                    .timestamp(wechatTimestamp)
                    .body(requestBody)
                    .build();
            // 4. 构建 RSAPublicKeyNotificationConfig
            NotificationConfig config = new RSAPublicKeyNotificationConfig.Builder()
                    .publicKey(publicKey)      // 微信支付公钥字符串
                    .publicKeyId(publicKeyId)     // 公钥ID（必须）
                    .apiV3Key(apiV3Key)                // APIv3密钥
                    .build();
            // 5. 初始化解析器
            NotificationParser parser = new NotificationParser(config);
            // 6. 验签并解密，直接转换为 Transaction 对象（微信支付订单对象）
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            // 9. 获取订单关键信息
            String outTradeNo = transaction.getOutTradeNo();   // 商户订单号
            String transactionId = transaction.getTransactionId(); // 微信支付订单号
            Transaction.TradeStateEnum tradeState = transaction.getTradeState(); // 支付状态
            // 其他字段请参考 Transaction 类
            log.info("支付回调成功: outTradeNo={}, transactionId={}, tradeState={}",
                    outTradeNo, transactionId, tradeState);

            // 9. 业务处理（幂等更新订单）
            boolean success = processOrder(outTradeNo, transactionId, tradeState, transaction);
            if (!success) {
                return "FAIL";
            }
            // 10. 返回 SUCCESS
            return "SUCCESS";

        } catch (Exception e) {
            log.error("回调处理异常", e);
            return "FAIL";
        }
    }

    /**
     * 生成订单号格式：OUT_TRADE_NO_yyyyMMdd_HHmmssSSS + 两位随机数
     * 例如：OUT_TRADE_NO_20250303_14353012345
     */
    private String generateOrderNo() {
        LocalDateTime now = LocalDateTime.now();
        String datePart = now.format(DATE_FORMATTER);
        String timePart = now.format(TIME_FORMATTER); // 精确到毫秒（3位）
        int randomSuffix = ThreadLocalRandom.current().nextInt(100); // 0-99 两位
        return PREFIX + datePart + "_" + timePart + String.format("%02d", randomSuffix);
    }

    private String insertMiniOrder(WechatMiniOrderDO wechatMiniOrderDO){
        if(CollectionUtils.isEmpty(wechatMiniOrderDO.getItems())){
            throw exceptionCustomMsg(ErrorCodeConstants.COMMON_ERROR,"明细不能为空");
        }
        StringBuilder description = new StringBuilder();
        wechatMiniOrderDO.setStatus(0);
        wechatMiniOrderMapper.insert(wechatMiniOrderDO);
        List<WechatMiniOrderItemDO> items = wechatMiniOrderDO.getItems();
        for (WechatMiniOrderItemDO item : items) {
            item.setOrderId(wechatMiniOrderDO.getId());
            description.append(item.getGoodsName()).append(",数量：").append(item.getQuantity())
                    .append(",单价：").append(item.getPrice())
                    .append(",金额：").append(item.getAmount()).append("。");
        }
        wechatMiniOrderItemMapper.insertBatch(items);
        return description.toString();
    }

    /**
     * 从 HttpServletRequest 中读取请求体（原始字符串）
     */
    private String getRequestBody(HttpServletRequest request) throws IOException {
        try (BufferedReader reader = request.getReader()) {
            return reader.lines().collect(Collectors.joining(System.lineSeparator()));
        }
    }

    private boolean processOrder(String outTradeNo, String transactionId, Transaction.TradeStateEnum tradeState, Transaction transaction) {
        try {
            if(tradeState.equals(Transaction.TradeStateEnum.SUCCESS)){
                wechatMiniOrderMapper.update(new LambdaUpdateWrapper<WechatMiniOrderDO>()
                        .eq(WechatMiniOrderDO::getOutTradeNo,outTradeNo)
                        .set(WechatMiniOrderDO::getStatus,1));
            }
            return true; // 成功返回 true
        } catch (Exception e) {
            log.error("业务处理失败", e);
            return false;
        }
    }
}
