package com.yiqi.notify;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.internal.util.AlipaySignature;
import com.yiqi.common.config.AlipayConfigs;
import com.google.gson.Gson;
import com.yiqi.common.config.WxPayConfigs;
import com.yiqi.common.core.controller.BaseController;
import com.yiqi.common.core.domain.R;
import com.yiqi.common.enums.OrderRefundStatus;
import com.yiqi.common.utils.pay.WechatPayValidator;
import com.yiqi.order.domain.dto.AlipayNotifyDTO;
import com.yiqi.order.service.IPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xiaohei
 * @date 2024/1/2
 */
@Slf4j
@Api(tags = "第三方支付相关")
@RestController
@RequestMapping(value = "pay/ali")
public class AliPayController extends BaseController {

    @Autowired
    private IPayService payService;

    @ApiOperation("支付宝支付回调")
    @PostMapping(value = "notify")
    public String alipayNotify(HttpServletRequest request) throws Exception {
        Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中
        String paramsJson = JSON.toJSONString(params);
        logger.info("支付宝回调，" + paramsJson);
        try {
            AlipayConfig alipayConfig = new AlipayConfig();// 支付宝配置
            // 调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfigs.getPublicKey(),
                    "GBK", AlipayConfigs.getSignType());
            if (signVerified) {
                logger.info("支付宝回调签名认证成功");
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                AlipayNotifyDTO alipayNotifyDTO = new Gson().fromJson(new com.alibaba.fastjson2.JSONObject(params).toString(), AlipayNotifyDTO.class);
                R result = this.checkAlipayOrder(alipayNotifyDTO);
                if (R.isError(result)) {
                    return "failure";
                }
                // 另起线程处理业务
                payService.unifiePaySuccessNotify(alipayNotifyDTO.getOut_trade_no(), null, alipayNotifyDTO.getTrade_no());
                return "success";
            } else {
                logger.info("支付宝回调签名认证失败，signVerified=false, paramsJson:" + paramsJson);
                return "failure";
            }
        } catch (AlipayApiException e) {
            logger.info("支付宝回调签名认证失败,paramsJson:" + paramsJson + ",errorMsg:" + e.getMessage());
            return "failure";
        }
    }

    /**
     * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
     * 2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
     * 3、校验通知中的seller_id（或者seller_email)是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
     * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。
     * 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
     * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
     *
     * @param alipayNotifyDTO
     * @throws AlipayApiException
     */
    private R checkAlipayOrder(AlipayNotifyDTO alipayNotifyDTO) throws Exception {
        return R.ok();
    }

    @PostMapping(value = "wxRefundOrder")
    @ApiModelProperty("微信退款通知回调")
    public Map<String, String> wxRefundOrder(HttpServletRequest request, HttpServletResponse response) {
        log.info("退款回调");

        // 处理通知参数
        Map<String, Object> bodyMap = getNotifyBody(request);
        if (bodyMap == null) {
            return falseMsg(response);
        }

        // 解密resource中的通知数据
        String resource = bodyMap.get("resource").toString();
        Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, WxPayConfigs.getApiV3Key(), 2);
        String orderNo = resourceMap.get("out_trade_no").toString();
//                String transactionId = resourceMap.get("transaction_id").toString();
        String refundStatus = resourceMap.get("refund_status").toString();
        payService.refundNotify(orderNo, "SUCCESS".equals(refundStatus) ? OrderRefundStatus.REFUND_SUCCESS.getCode()
                : ("CLOSED".equals(refundStatus) ? OrderRefundStatus.REFUND_CANCEL.getCode() : OrderRefundStatus.ERROR.getCode()));
        log.info("退款所有参数" + resourceMap);

        // TODO 根据订单号，做幂等处理，并且在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱
        // 更改订单状态为已退款
        log.warn("=========== 根据订单号，做幂等处理 ===========");
        return trueMsg(response);
    }

    private Map<String, Object> getNotifyBody(HttpServletRequest request) {
        //处理通知参数
        String body = WechatPayValidator.readData(request);
        log.info("退款回调参数：{}", body);

        // 转换为Map
        Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>() {
        });
        // 微信的通知ID（通知的唯一ID）
        String notifyId = bodyMap.get("id").toString();
        // 验证签名信息
        WechatPayValidator wechatPayValidator
                = new WechatPayValidator(WxPayConfigs.getVerifier(), notifyId, body);
        if (!wechatPayValidator.validate(request)) {

            log.error("通知验签失败");
            return null;
        }
        log.info("通知验签成功");
        return bodyMap;
    }

    private Map<String, String> falseMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //失败应答
        response.setStatus(500);
        resMap.put("code", "ERROR");
        resMap.put("message", "通知验签失败");
        return resMap;
    }

    private Map<String, String> trueMsg(HttpServletResponse response) {
        Map<String, String> resMap = new HashMap<>(8);
        //成功应答
        response.setStatus(200);
        resMap.put("code", "SUCCESS");
        resMap.put("message", "成功");
        return resMap;
    }
}
