package com.starry.admin.modules.weichat.controller;


import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import com.starry.admin.common.aspect.CustomUserLogin;
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.order.service.impl.PlayOrderInfoServiceImpl;
import com.starry.admin.modules.platform.entity.SysTenantEntity;
import com.starry.admin.modules.platform.service.impl.SysTenantServiceImpl;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.result.R;
import com.starry.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author admin
 */
@Slf4j
@RestController
@RequestMapping("/wx/pay/")
public class WxPlayController {

    @Resource
    private SysTenantServiceImpl tenantService;
    @Resource
    private IPlayCustomUserInfoService customUserInfoService;
    @Resource
    private IPlayOrderInfoService orderInfoService;

    @Resource
    PlayOrderInfoServiceImpl playOrderInfoService;
    @Resource
    private WxCustomMpService mpService;


    //@GetMapping("/test")
    //public String test() {
    //    SysTenantEntity tenant = tenantService.getById("7f7ecd75fd29464db2867982f88e8ef0");
    //    tenantService.initRole(tenant);
    //    return WxPayNotifyResponse.success("成功");
    //}

    /**
     * 微信支付-微信回调地址（https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_7&index=8）
     *
     * @author admin
     * @since 2024/5/8 11:25
     **/
    @RequestMapping("/jsCallback")
    public String wxPayNotify(HttpServletRequest request) {
        try (InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inStream.read(buffer)) != -1) {
                outSteam.write(buffer, 0, len);
            }
            log.info("微信支付回调开始-参数解析-开始");
            // 获取微信调用我们notify_url的返回信息
            String result = outSteam.toString("utf-8");
            log.info("微信支付回调开始-参数解析-结束,result={}", result);
            this.dealNotify(result);
        } catch (Exception e) {
            log.error("微信支付回调业务处理异常", e);
        }
        return WxPayNotifyResponse.success("成功");
    }


    public void dealNotify(String xmlData) {
        try {
            Map<String, String> orderMap = readStringXmlOut(xmlData);
            String outTradeNo = orderMap.get("out_trade_no");
            String tenantId = orderMap.get("attach");
            if (StrUtil.isBlank(tenantId)) {
                log.error("支付对应业务处理异常-未查询到租户信息,xmlData={}", xmlData);
            }
            SecurityUtils.setTenantId(tenantId);
            PlayOrderInfoEntity orderInfoEntity = orderInfoService.queryByOrderNo(outTradeNo);
            if (Objects.isNull(orderInfoEntity)) {
                log.error("支付对应业务处理异常-未查询到对应的支付记录，xmlData={}", xmlData);
                return;
            }
            // TODO如果订单不是支付订单，并且支付状态不为未支付
            if (!"0".equals(orderInfoEntity.getOrderType()) || !"0".equals(orderInfoEntity.getPayState())) {
                log.error("支付对应业务处理异常-支付记录状态异常，支付记录：xmlData={}", xmlData);
                return;
            }
            //修养订单状态
            orderInfoEntity.setPayState("1");
            orderInfoService.updateById(orderInfoEntity);
            //修改账户余额
            customUserInfoService.customAccountBalanceRecharge(orderInfoEntity.getOrderMoney(), orderInfoEntity.getPurchaserBy(), orderInfoEntity.getId());
            log.info("*********支付处理完成");
            // 推送通知
            mpService.sendBalanceMessage(orderInfoEntity);
        } catch (Exception e) {
            log.error("订单回调业务处理异常,xmlData={}", xmlData);
        }
    }

    @GetMapping("/test")
    public R test() {

        PlayOrderInfoEntity orderInfo = playOrderInfoService.getById("0e3e7d7ec69a");
        mpService.sendBalanceMessage(orderInfo);
        return R.ok(true);
    }


    @CustomUserLogin
    @GetMapping("/custom/getCustomPaymentAmount")
    public R getCustomPaymentAmount(@RequestParam("money") String money) {
        if (StringUtils.isEmpty(money)) {
            throw new CustomException("请求参数错误,money不能为空");
        }
        BigDecimal paymentAmount = customUserInfoService.getCustomPaymentAmount(new BigDecimal(money), ThreadLocalRequestDetail.getCustomUserInfo().getId());
        return R.ok(paymentAmount);
    }

    @CustomUserLogin
    @GetMapping("/custom/createOrder")
    public R createOrder(@RequestParam("money") String money) {
        if (StringUtils.isEmpty(money)) {
            throw new CustomException("请求参数错误,money不能为空");
        }
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlankIfStr(tenantId)) {
            throw new CustomException("系统错误,租户ID获取失败");
        }
        // 用户信息
        PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(ThreadLocalRequestDetail.getCustomUserInfo().getId());
        // 租户信息
        SysTenantEntity entity = tenantService.selectSysTenantByTenantId(tenantId);
        // 订单总金额，单位为分
        long totalFee = getTotalFee(money);
        // 创建订单信息
        String orderNo = playOrderInfoService.getOrderNo();
        orderInfoService.createRechargeOrder(orderNo, new BigDecimal(totalFee * 1.0 / 100), new BigDecimal(totalFee * 1.0 / 100), customUserInfo.getId());


        WxPayService wxPayService = mpService.getWxPay();
        WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
        request.setOpenid(customUserInfo.getOpenid());
        // 订单总金额，单位为分（开发阶段固定设置为支付1分钱）
        request.setAttach(tenantId);
        request.setTotalFee((int) totalFee);
        request.setOutTradeNo(orderNo);
        request.setTradeType("JSAPI");
        request.setSpbillCreateIp("101.43.206.16");
        request.setNotifyUrl(wxPayService.getConfig().getNotifyUrl());
        request.setBody("船票充值");
        WxPayUnifiedOrderResult orderResult;
        try {
            //统一下单，商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再在APP里面调起支付
            orderResult = wxPayService.unifiedOrder(request);
        } catch (WxPayException e) {
            throw new RuntimeException(e);
        }
//
        String prepayId = orderResult.getPrepayId();
        //组合参数构建支付
        Map<String, String> paySignInfo = new HashMap<>(5);
        String timeStamp = String.valueOf(System.currentTimeMillis());
        String nonceStr = "dalfhh241lnandnsklajax";
        paySignInfo.put("appId", wxPayService.getConfig().getAppId());
        paySignInfo.put("nonceStr", nonceStr);
        paySignInfo.put("timeStamp", timeStamp);
        paySignInfo.put("signType", "MD5");
        paySignInfo.put("package", "prepay_id=" + prepayId);
        String[] signInfo = new String[0];
        String paySign = SignUtils.createSign(paySignInfo, "MD5", wxPayService.getConfig().getMchKey(), signInfo);

        //组合支付参数
        JSONObject jsonObject = new JSONObject().fluentPut("appId", wxPayService.getConfig().getAppId()).fluentPut("timeStamp", timeStamp).fluentPut("nonceStr", nonceStr).fluentPut("package", "prepay_id=" + prepayId).fluentPut("signType", "MD5").fluentPut("paySign", paySign);
        return R.ok(jsonObject);
    }


    /**
     * @param money 金额,单位-元
     * @return 金额, 单位-分
     */
    public long getTotalFee(String money) {
        long totalFee;
        try {
            totalFee = (long) (Float.parseFloat(money) * 100);
        } catch (Exception e) {
            throw new CustomException("请求参数错误,money类型异常");
        }
        if (totalFee <= 0 || totalFee > 1000000) {
            throw new CustomException("请求参数错误,单笔支付金额必须大于0，且小于10000");
        }
        return totalFee;
    }

    public static Map<String, String> readStringXmlOut(String xml) {
        Map<String, String> map = new HashMap<String, String>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xml); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            List<Element> list = rootElt.elements();//获取根节点下所有节点
            for (Element element : list) {  //遍历节点
                map.put(element.getName(), element.getText()); //节点的name为map的key，text为map的value
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
        // 参数示例
        //<xml>
        //    <appid><![CDATA[wx846289ab0b2e9f89]]></appid>
        //    <bank_type><![CDATA[OTHERS]]></bank_type>
        //    <cash_fee><![CDATA[1]]></cash_fee>
        //    <fee_type><![CDATA[CNY]]></fee_type>
        //    <is_subscribe><![CDATA[N]]></is_subscribe>
        //    <mch_id><![CDATA[1616729997]]></mch_id>
        //    <nonce_str><![CDATA[1692338418306]]></nonce_str>
        //    <openid><![CDATA[oNHh06-KauVEor1BfSWsBr2Xil-I]]></openid>
        //    <out_trade_no><![CDATA[gameno20230818140018j5kc]]></out_trade_no>
        //    <result_code><![CDATA[SUCCESS]]></result_code>
        //    <return_code><![CDATA[SUCCESS]]></return_code>
        //    <sign><![CDATA[639C5B46EB11B42A354E38A5F96683CC]]></sign>
        //    <time_end><![CDATA[20230818140028]]></time_end>
        //    <total_fee>1</total_fee>
        //    <trade_type><![CDATA[JSAPI]]></trade_type>
        //    <transaction_id><![CDATA[4200001910202308187726172728]]></transaction_id>
        //</xml>
    }
}
