package com.ruoyi.web.common;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.bst.app.domain.AppVO;
import com.ruoyi.bst.app.service.AppService;
import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.channel.service.ChannelConverter;
import com.ruoyi.bst.channel.service.ChannelService;
import com.ruoyi.bst.pay.service.PayService;
import com.ruoyi.bst.refund.service.RefundService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.pay.ali.enums.AliTradeStatusEnum;
import com.ruoyi.common.pay.ali.service.AliPayService;
import com.ruoyi.common.pay.tm.TmPayService;
import com.ruoyi.common.pay.tm.enums.TmPayStatus;
import com.ruoyi.common.pay.wx.config.WxPayConfig;
import com.ruoyi.common.pay.wx.domain.enums.WxNotifyEventType;
import com.ruoyi.common.pay.wx.util.WxPayUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.http.HttpUtils;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import com.wechat.pay.java.service.refund.model.Status;

import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

/**
 * @author wjh
 * 2025/3/27
 */
@RestController
@RequestMapping("/pay/notify")
@Slf4j
public class PayNotifyController extends BaseController {

    @Autowired
    private ChannelConverter channelConverter;

    @Autowired
    private PayService payService;

    @Autowired
    private AppService appService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private TmPayService tmPayService;

    @Autowired
    private AliPayService aliPayService;

    @Autowired
    private RefundService refundService;

    // 微信支付结果通知
    @ApiOperation("微信支付通知")
    @PostMapping("/wx/{channelId}/{appId}")
    @Anonymous
    public ResponseEntity<Boolean> wxPayNotify(HttpServletRequest request, @PathVariable Long channelId, @PathVariable Long appId) {
        try {
            log.info("收到微信支付回调{}", request);

            WxPayConfig wxPayConfig = channelConverter.toWxPayConfig(channelId, appId);
            ServiceUtil.assertion(wxPayConfig == null, "转为微信支付配置失败");

            // 转为微信支付业务结果对象
            Transaction transaction = WxPayUtil.toTransaction(request, wxPayConfig);

            // 支付成功
            if (WxPayUtil.isSuccess(transaction)) {
                payService.handleSuccess(transaction.getOutTradeNo(), DateUtils.toLocalDateTimeByISO(transaction.getSuccessTime()));
            }
        } catch (ValidationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
        return ResponseEntity.status(HttpStatus.OK).body(null);
    }

    // 微信退款回调
    @PostMapping("/wx/refund/{channelId}/{appId}")
    @Anonymous
    public ResponseEntity<Boolean> wxPayNotifyRefund(HttpServletRequest request,  @PathVariable Long channelId, @PathVariable Long appId) {
        try {
            WxPayConfig wxPayConfig = channelConverter.toWxPayConfig(channelId, appId);
            ServiceUtil.assertion(wxPayConfig == null, "转为微信支付配置失败");

            String body = HttpUtils.getBody(request);
            log.info("【微信退款回调】接收对象 : " + JSON.toJSONString(body));
            // 解析通知数据
            Notification notification = JSON.parseObject(body, Notification.class);
            log.info("【微信退款回调】转换成notification: " + JSON.toJSONString(notification));
            // 退款成功通知
            if (WxNotifyEventType.REFUND_SUCCESS.getValue().equals(notification.getEventType())) {
                // 验签、解密并转换成 RefundNotification
                RefundNotification refundNotification = WxPayUtil.checkAndParse(request, body, RefundNotification.class, wxPayConfig);
                log.info("【微信退款回调】转换成RefundNotification: " + JSON.toJSONString(refundNotification));
                if (Status.SUCCESS.equals(refundNotification.getRefundStatus())) {
                    // 退款成功操作
                    String outRefundNo = refundNotification.getOutRefundNo();
                    refundService.handleRefundSuccess(outRefundNo);
                }
            }
        } catch (ValidationException e) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(null);
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
        return ResponseEntity.status(HttpStatus.OK).body(null);
    }


    /**
     * 太米微信支付回调
     */
    @ApiOperation(value = "太米微信支付回调")
    @PostMapping("/tm/{channelId}")
    public String tmwx(HttpServletRequest request, @PathVariable Long channelId) {
        try {
            String body = HttpUtils.getBody(request);
            log.info("【太米微信支付回调】接收对象 : " + body);
            // 先把body转成map
            Map<String, Object> params = JSON.parseObject(body, Map.class);
            // 验证签名
            ChannelVO channel = channelService.selectChannelByChannelId(channelId);
            ServiceUtil.assertion(channel == null || channel.getChannelConfig() == null, "渠道ID为%s的配置为空", channelId);
            boolean sign = tmPayService.validSign(params, channel.getChannelConfig().getSignKey());
            if (sign) {
                JSONObject tradeInfo = (JSONObject)params.get("tradeInfo");
                String payType = tradeInfo.getString("payType"); // 交易类型
                String outTradeId = tradeInfo.getString("outTradeId");   // 商户自定义订单号
                String payStatus = tradeInfo.getString("payStatus");    // 交易结果
                if(TmPayStatus.isSuccess(payStatus) && payType.equals("wx_pay")) {
                    payService.handleSuccess(outTradeId, DateUtils.toLocalDateTime(tradeInfo.getDate("payTime")));
                }
            }else {
                throw new ServiceException("签名验证失败");
            }
            return "{\"result\":\"SUCCESS\"}";
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

    @ApiOperation("支付宝支付通知")
    @PostMapping("/ali/{appId}")
    @Anonymous
    public String aliPayNotify(HttpServletRequest request, @PathVariable Long appId) {
        try {
            log.info("收到支付宝支付通知{}", request);
            Map<String, String> params = HttpUtils.convertRequestParamsToMap(request);

            AppVO app = appService.selectAppById(appId);
            ServiceUtil.assertion(app == null || app.getConfig() == null, "ID为%s的应用不存在", appId);
            aliPayService.checkNotifySign(params, app.getConfig().getAlipayCertPath());

            log.info("收到支付宝支付通知{}", params);

            // 交易状态
            String tradeStatus = params.get("trade_status");

            // 交易状态不为已支付
            if (!AliTradeStatusEnum.notifySuccessList().contains(tradeStatus)) {
                return "fail";
            }

            // 判断是否是退款订单
            String refundFee = params.get("refund_fee");
            // 退款订单
            if (StrUtil.isNotBlank(refundFee)) {
                // 商家业务号，主要是退款单号
                String outBizNo = params.get("out_biz_no");
                refundService.handleRefundSuccess(outBizNo);
            }
            // 支付订单
            else {
                //商户订单号
                String outTradeNo = params.get("out_trade_no");
                payService.handleSuccess(outTradeNo, DateUtils.toLocalDateTime(params.get("gmt_payment")));
            }
        } catch (Exception e) {
            return "fail";
        }

        return "success";
    }
}
