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

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverResult;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult;
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.system.module.entity.SysTenantEntity;
import com.starry.admin.modules.system.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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
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;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
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;

/**
 * @author admin
 */
@Api(tags = "微信支付接口", description = "微信端支付相关接口")
@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;

    /**
     * 微信支付-微信回调地址（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
     **/
    @ApiOperation(value = "微信支付回调", notes = "微信支付后的回调接口")
    @ApiResponses({@ApiResponse(code = 200, message = "成功")})
    @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);
            // 请求分账
            WxPayService wxPayService = mpService.getWxPay();
            this.profitSharing(wxPayService, orderMap, orderInfoEntity, tenantService.selectSysTenantByTenantId(orderInfoEntity.getTenantId()));
        } catch (Exception e) {
            log.error("订单回调业务处理异常,xmlData={}", xmlData);
        }
    }

    @ApiOperation(value = "测试消息推送", notes = "测试充值消息推送功能")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功")})
    @GetMapping("/test")
    public R test() {
        PlayOrderInfoEntity orderInfo = playOrderInfoService.getById("0e3e7d7ec69a");
        mpService.sendBalanceMessage(orderInfo);
        return R.ok(true);
    }

    @ApiOperation(value = "获取支付金额", notes = "根据充值金额获取实际支付金额")
    @ApiImplicitParam(name = "money", value = "充值金额", required = true, dataType = "String", paramType = "query")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功"), @ApiResponse(code = 500, message = "请求参数错误,money不能为空")})
    @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);
    }

    @ApiOperation(value = "创建充值订单", notes = "创建微信支付充值订单")
    @ApiImplicitParam(name = "money", value = "充值金额", required = true, dataType = "String", paramType = "query")
    @ApiResponses({@ApiResponse(code = 200, message = "操作成功"), @ApiResponse(code = 500, message = "请求参数错误,money不能为空"), @ApiResponse(code = 500, message = "充值金额不能小于1元"), @ApiResponse(code = 500, message = "系统错误,租户ID获取失败")})
    @CustomUserLogin
    @GetMapping("/custom/createOrder")
    public R createOrder(@RequestParam("money") String money) {
        if (StringUtils.isEmpty(money)) {
            throw new CustomException("请求参数错误,money不能为空");
        }
        if (new BigDecimal(money).compareTo(new BigDecimal("1")) < 0) {
            throw new CustomException("充值金额不能小于1元");
        }
        String tenantId = SecurityUtils.getTenantId();
        if (StrUtil.isBlankIfStr(tenantId)) {
            throw new CustomException("系统错误,租户ID获取失败");
        }
        // 用户信息
        PlayCustomUserInfoEntity customUserInfo = customUserInfoService.selectById(ThreadLocalRequestDetail.getCustomUserInfo().getId());
        // 租户信息
        SysTenantEntity tenant = tenantService.selectSysTenantByTenantId(tenantId);
        mpService.checkSubscribeThrowsExp(customUserInfo.getOpenid(), tenant.getTenantId());
        Boolean profitSharing = tenant.getProfitsharingRate() > 0;
        // 订单总金额，单位为分
        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("船票充值");
        if (profitSharing) {
            // 添加分账方
            this.profitSharingAddreceiver(wxPayService);
            request.setProfitSharing("Y");
        }

        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);
    }

    private void profitSharingAddreceiver(WxPayService wxPayService) {
        ProfitSharingReceiverRequest request = new ProfitSharingReceiverRequest();
        request.setAppid(wxPayService.getConfig().getAppId());
        request.setMchId(wxPayService.getConfig().getMchId());
        String nonceStr = "dalfhh241lnandnsklajax";
        request.setNonceStr(nonceStr);
        request.setSignType("HMAC-SHA256");
        request.setReceiver(new JSONObject().fluentPut("type", "MERCHANT_ID").fluentPut("account", "1681470208").fluentPut("name", "合肥经济技术开发区陪陪信息技术服务部（个体工商户）").fluentPut("relation_type", "SERVICE_PROVIDER").toString());
        try {
            ProfitSharingReceiverResult result = wxPayService.getProfitSharingService().addReceiver(request);
            log.info("请求添加分账结果：" + JSONObject.toJSONString(result));
        } catch (WxPayException e) {
            throw new RuntimeException(e);
        }

    }

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

    private void profitSharing(WxPayService wxPayService, Map<String, String> orderMap, PlayOrderInfoEntity orderInfoEntity, SysTenantEntity tenant) throws WxPayException {
        Boolean profitSharing = tenant.getProfitsharingRate() > 0;
        if (!profitSharing) {
            return;
        }
        log.info("开始进行分账。。。。。。。。。。。。。。。");
        long totalFee = getTotalFee(orderInfoEntity.getOrderMoney().toString());
        int amount = new BigDecimal(totalFee).multiply(BigDecimal.valueOf((double) tenant.getProfitsharingRate() / 100)).intValue();
        if (amount == 0) {
            return;
        }
        ProfitSharingRequest request = new ProfitSharingRequest();
        request.setSignType("HMAC-SHA256");
        request.setTransactionId(orderMap.get("transaction_id"));
        request.setOutOrderNo("ps" + orderMap.get("out_trade_no"));

        request.setReceivers(new JSONArray().fluentAdd(new JSONObject().fluentPut("type", "MERCHANT_ID").fluentPut("name", "合肥经济技术开发区陪陪信息技术服务部（个体工商户）").fluentPut("account", "1681470208").fluentPut("amount", amount)).toString());
        ProfitSharingResult result = wxPayService.getProfitSharingService().profitSharing(request);
        log.info("分账结果：" + JSONObject.toJSONString(result));
        // 保存分账金额
        BigDecimal profitSharingAmount = new BigDecimal(amount).divide(new BigDecimal(100));
        orderInfoService.update(null, Wrappers.lambdaUpdate(PlayOrderInfoEntity.class).eq(PlayOrderInfoEntity::getId, orderInfoEntity.getId()).set(PlayOrderInfoEntity::getProfitSharingAmount, profitSharingAmount));
    }

}
