package com.ruoyi.web.controller.app;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
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.syb.enums.SybTrxCode;
import com.ruoyi.common.pay.syb.enums.SybTrxStatus;
import com.ruoyi.common.pay.syb.service.SybPayService;
import com.ruoyi.common.pay.syb.util.SybUtil;
import com.ruoyi.common.pay.tm.TmPayService;
import com.ruoyi.common.pay.tm.enums.TmPayStatus;
import com.ruoyi.common.pay.wx.domain.enums.WxNotifyEventType;
import com.ruoyi.common.pay.wx.domain.enums.WxTransferBatchStatus;
import com.ruoyi.common.pay.wx.util.WxPayUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.ss.payBill.service.PayBillService;
import com.ruoyi.ss.refund.service.RefundService;
import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO;
import com.ruoyi.ss.channel.domain.enums.ChannelEnum;
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.transactionBill.service.TransactionBillConverter;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.common.pay.wx.domain.enums.AttachEnums;
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 com.wechat.pay.java.service.transferbatch.model.TransferBatchGet;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author 辉
 * 2024/3/11
 */
@Api(tags = "支付")
@RestController
@RequestMapping("/app/pay")
@Slf4j
public class AppPayController extends BaseController {

    @Autowired
    private TransactionBillService transactionBillService;

    @Autowired
    private RefundService refundService;

    @Autowired
    private PayBillService payBillService;

    @Autowired
    private SybPayService sybPayService;

    @Autowired
    private TransactionBillConverter transactionBillConverter;

    @Autowired
    private TmPayService tmPayService;

    @Autowired
    private AliPayService aliPayService;

    @ApiOperation("微信支付充值订单")
    @GetMapping("/wx/{billNo}")
    public AjaxResult wxPay(@PathVariable @ApiParam("订单编号") String billNo) {
        TransactionBillVO bill = transactionBillService.selectSmTransactionBillByBillNo(billNo);
        if (bill == null || bill.getUserId() == null || !bill.getUserId().equals(getUserId())) {
            return error("这不是您的订单");
        }
        RechargePayBO bo = transactionBillConverter.toRechargePayBO(bill, ChannelEnum.WECHAT.getType());
        return success(transactionBillService.pay(bo));
    }

    // 微信支付结果通知
    @ApiOperation("微信支付通知")
    @PostMapping("/notify/wx")
    @Anonymous
    public ResponseEntity<Boolean> wxPayNotify(HttpServletRequest request) {
        try {
            log.info("收到微信支付回调{}", request);
            // 转为微信支付业务结果对象
            Transaction transaction = WxPayUtil.toTransaction(request);

            // 支付成功
            if (WxPayUtil.isSuccess(transaction)) {
                // 根据附加信息，判断是哪个订单
                String attach = transaction.getAttach();
                if (AttachEnums.PAY_BILL.getAttach().equals(attach)){
                     // 新版支付订单
                     payBillService.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("/notify/wx/refund")
    @Anonymous
    public ResponseEntity<Boolean> wxPayNotifyRefund(HttpServletRequest request) {
        try {
            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);
                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("微信转账到零钱回调")
    @PostMapping("/notify/wx/transfer")
    @Anonymous
    public ResponseEntity<Boolean> wxTransferNotify(HttpServletRequest request) {
        try {
            log.info("收到微信转账到零钱回调{}", request);
            String body = HttpUtils.getBody(request);
            // 解析通知数据
            Notification notification = JSON.parseObject(body, Notification.class);

            // 商户转账成功通知
            if (WxNotifyEventType.MCHTRANSFER_BATCH_FINISHED.getValue().equals(notification.getEventType())) {
                // 验签、解密并转换成 TransferBatchGet
                TransferBatchGet transferBatchGet = WxPayUtil.checkAndParse(request, body, TransferBatchGet.class);
                // 修改订单状态
                if (WxTransferBatchStatus.FINISHED.getStatus().equals(transferBatchGet.getBatchStatus())) {
                    // TODO 提现成功
                } else if (WxTransferBatchStatus.CLOSED.getStatus().equals(transferBatchGet.getBatchStatus())) {
                    // TODO 提现失败
                }
            }
        } 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("/notify/tl")
    @Anonymous
    public String tlPayNotify(HttpServletRequest request) {
        try {
            request.setCharacterEncoding("UTF-8");//通知传输的编码为GBK
            // 获取所有参数
            TreeMap<String, String> params = SybUtil.getParams(request);
            boolean sign = sybPayService.validSign(params);
            if (sign) {
                String trxCode = params.get("trxcode"); // 交易类型
                String cusOrderId = params.get("cusorderid");   // 商户自定义订单号
                String trxStatus = params.get("trxstatus");    // 交易结果

                if (SybTrxStatus.isSuccess(trxStatus)) {
                    // 微信支付通知
                    if (SybTrxCode.WX_PAY.equalsCode(trxCode)) {
                        // 新版支付订单
                        payBillService.handleSuccess(cusOrderId, DateUtils.toLocalDateTime(params.get("paytime"), "yyyyMMddHHmmss"));
                    }
                    // 微信退款通知
                    else if (SybTrxCode.WX_REFUND.equalsCode(trxCode)) {
                        refundService.handleRefundSuccess(cusOrderId);
                    }
                }
            }
            return "error";
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }


    /**
     * 太米微信支付回调
     */
    @ApiOperation(value = "太米微信支付回调")
    @PostMapping("/notify/tm")
    public String tmwx(HttpServletRequest request) {
        try {
            String body = HttpUtils.getBody(request);
            log.info("【太米微信支付回调】接收对象 : " + body);
            // 先把body转成map
            Map<String, Object> params = JSON.parseObject(body, Map.class);
            // 验证签名
            boolean sign = tmPayService.validSign(params);
            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")) {
                    payBillService.handleSuccess(outTradeId, DateUtils.toLocalDateTime(tradeInfo.getDate("payTime")));
                }
            }else {
                throw new ServiceException("签名验证失败");
            }
            return "{\"result\":\"SUCCESS\"}";
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
    }

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

            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");
                payBillService.handleSuccess(outTradeNo, DateUtils.toLocalDateTime(params.get("gmt_payment")));
            }
        } catch (Exception e) {
            return "fail";
        }


        return "success";
    }

    @ApiOperation("支付宝转账通知")
    @PostMapping("/notify/ali/transfer")
    @Anonymous
    public ResponseEntity<Boolean> aliPayNotifyTransfer(HttpServletRequest request) {
        try {
            log.info("收到支付宝转账通知{}", request);
            String body = HttpUtils.getBody(request);
            log.info("收到支付宝转账通知{}", body);

            // TODO 似乎不生效
        } 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);
    }

}
