package com.yiqi.notify;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConfig;
import com.alipay.api.internal.util.AlipaySignature;
import com.google.gson.Gson;
import com.ijpay.core.kit.WxPayKit;
import com.yiqi.common.config.AlipayConfigs;
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 com.yiqi.payment.WechatPayService;
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.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

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

    @Autowired
    private IPayService payService;

    @ApiOperation("微信支付回调")
    @PostMapping(value = "notify")
    public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //处理通知参数
        String body = WechatPayValidator.readData(request);
        log.info("微信回调参数：{}", body);

        // 转换为Map
        Map<String, String> params = WxPayKit.xmlToMap(body);
        boolean check = WxPayKit.verifyNotify(params, WechatPayService.pay_key);

        if (!check) {
            logger.error("微信验签失败");
            toRes("FAIL", "验签失败", response);
            return;
        }
        // 读取输入流
        if (params.get("return_code").equals("SUCCESS")) {
            if (params.get("result_code").equals("SUCCESS")) {
                String orderSn = params.get("out_trade_no");
                String outTradeSn = params.get("transaction_id");
                String buyerUserId = params.get("open_id");
                //使用订单号当锁
                synchronized (orderSn.intern()) {
                    // 另起线程处理业务
                    payService.unifiePaySuccessNotify(orderSn, null, outTradeSn);
                }
                logger.info("回复成功！！！");
                toRes("SUCCESS", "OK", response);
                return;
            }
        } else {
            toRes("FAIL", "payment fail", response);
        }
    }

    /**
     * 充值回调返回值
     *
     * @param code     return_code 请参照微信API中的格式
     * @param msg      return_msg 请参照微信API中的格式
     * @param response HttpServletResponse 只有用这种方法微信那边才认可为合法回应
     */
    private void toRes(String code, String msg, HttpServletResponse response) {
        try {
            response.reset();
            PrintWriter printWriter = response.getWriter();
            printWriter.write("<xml><return_code><![CDATA[" + code + "]]></return_code><return_msg><![CDATA[" + msg + "]]></return_msg></xml>");
            printWriter.flush();
            printWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 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;
    }
}
