package org.aibidding.system.controller.system;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.Event;
import com.stripe.model.Price;
import com.stripe.model.Product;
import com.stripe.model.checkout.Session;
import com.stripe.net.Webhook;
import com.stripe.param.checkout.SessionCreateParams;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aibidding.common.config.PayConfig;
import org.aibidding.common.core.domain.R;
import org.aibidding.common.core.exception.base.BaseException;
import org.aibidding.common.core.service.ConfigService;
import org.aibidding.common.core.utils.StringUtils;
import org.aibidding.common.oss.core.OssClient;
import org.aibidding.common.oss.entity.UploadResult;
import org.aibidding.common.oss.factory.OssFactory;
import org.aibidding.common.response.PayResponse;
import org.aibidding.common.service.PayService;
import org.aibidding.common.utils.MD5Util;
import org.aibidding.system.domain.bo.PaymentOrdersBo;
import org.aibidding.system.domain.bo.SysUserBo;
import org.aibidding.system.domain.request.OrderRequest;
import org.aibidding.system.domain.vo.PaymentOrdersVo;
import org.aibidding.system.domain.vo.SysUserVo;
import org.aibidding.system.service.IPaymentOrdersService;
import org.aibidding.system.service.ISysUserService;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@RequiredArgsConstructor
@RestController
@RequestMapping("/pay")
@Slf4j
public class PayController {

    private final PayService payService;

    private final ISysUserService userService;

    private final IPaymentOrdersService paymentOrdersService;

    private final PayConfig payConfig;

    private final WxPayService wxService;

    private final ConfigService configService;

    /**
     * 获取支付二维码
     *
     * @Date 2023/7/3
     * @return void
     **/
    @PostMapping("/payUrl")
    public R<PaymentOrdersVo> payUrl(@RequestBody OrderRequest orderRequest) {
        PaymentOrdersBo payOrder = paymentOrdersService.createPayOrder(orderRequest);
        PaymentOrdersVo paymentOrdersVo = new PaymentOrdersVo();
        if(!Boolean.parseBoolean(getKey("enabled"))){
            String payUrl = payService.getPayUrl(payOrder.getOrderNo(), orderRequest.getName(), Double.parseDouble(orderRequest.getMoney()), "192.168.1.6");
            byte[] bytes = QrCodeUtil.generatePng(payUrl, 300, 300);
            OssClient storage = OssFactory.instance();
            UploadResult upload=storage.upload(bytes, storage.getPath("qrCode",".png"), "image/png");
            BeanUtil.copyProperties(payOrder,paymentOrdersVo);
            paymentOrdersVo.setUrl(upload.getUrl());
        }else {
            WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
            request.setTradeType("NATIVE");
            request.setBody(orderRequest.getName());
            request.setOutTradeNo(payOrder.getOrderNo());
            request.setTotalFee(BaseWxPayRequest.yuanToFen(orderRequest.getMoney()));
            request.setSpbillCreateIp("127.0.0.1");
            request.setNotifyUrl(getKey("notifyUrl"));
            request.setProductId(payOrder.getId().toString());
            try {
                WxPayNativeOrderResult order = wxService.createOrder(request);
                byte[] bytes = QrCodeUtil.generatePng(order.getCodeUrl(), 300, 300);
                OssClient storage = OssFactory.instance();
                UploadResult upload = storage.upload(bytes, storage.getPath("qrCode",".png"), "image/png");
                BeanUtil.copyProperties(payOrder,paymentOrdersVo);
                paymentOrdersVo.setUrl(upload.getUrl());
            } catch (WxPayException e) {
                throw new BaseException("获取微信支付二维码发生错误：{}"+e.getMessage());
            }
        }
        return R.ok(paymentOrdersVo);
    }

    /**
     * 回调通知地址
     *
     * @Date 2023/7/3
     * @param
     * @return void
     **/
    @GetMapping("/notifyUrl")
    public String notifyUrl(PayResponse payResponse) {
        // 校验签名
        String mdString = "money=" + payResponse.getMoney() + "&name=" + payResponse.getName() +
            "&out_trade_no=" + payResponse.getOut_trade_no() + "&pid=" + payConfig.getPid() +
            "&trade_no=" + payResponse.getTrade_no() + "&trade_status=" + payResponse.getTrade_status() +
            "&type=" + payResponse.getType() +  payConfig.getKey();
        String sign = MD5Util.GetMD5Code(mdString);
        if(!sign.equals(payResponse.getSign())){
            throw new BaseException("校验签名失败！");
        }
        double money = Double.parseDouble(payResponse.getMoney());
        log.info("支付订单号{}",payResponse);
        PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo();
        paymentOrdersBo.setOrderNo(payResponse.getOut_trade_no());
        List<PaymentOrdersVo> paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo);
        if (CollectionUtil.isEmpty(paymentOrdersList)){
            throw new BaseException("订单不存在！");
        }
        // 订单状态修改为已支付
        PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0);
        paymentOrdersVo.setPaymentStatus("2");
        paymentOrdersVo.setPaymentMethod(payResponse.getType());
        BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
        paymentOrdersService.updateByBo(paymentOrdersBo);

        SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
        sysUserVo.setUserBalance(sysUserVo.getUserBalance() + money);
        SysUserBo sysUserBo = new SysUserBo();
        BeanUtil.copyProperties(sysUserVo,sysUserBo);
        // 设置为付费用户
        sysUserBo.setUserGrade("1");
        userService.updateUser(sysUserBo);
        return "success";
    }

    /**
     * 跳转通知地址
     *
     * @Date 2023/7/3
     * @param
     * @return void
     **/
    @GetMapping("/return_url")
    public void returnUrl() {
        log.info("return_url===========");
    }


    @PostMapping("/notify/wxOrder")
    public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {
        WxPayOrderNotifyResult notifyResult = this.wxService.parseOrderNotifyResult(xmlData);
        // TODO 根据自己业务场景需要构造返回对象
        PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo();
        paymentOrdersBo.setOrderNo(notifyResult.getOutTradeNo());
        List<PaymentOrdersVo> paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo);
        PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0);
        paymentOrdersVo.setPaymentStatus("2");
        paymentOrdersVo.setPaymentMethod("wx");
        BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
        paymentOrdersService.updateByBo(paymentOrdersBo);
        SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
        sysUserVo.setUserBalance(sysUserVo.getUserBalance() + convertCentsToYuan(notifyResult.getTotalFee()));
        SysUserBo sysUserBo = new SysUserBo();
        BeanUtil.copyProperties(sysUserVo,sysUserBo);
        // 设置为付费用户
        sysUserBo.setUserGrade("1");
        userService.updateUser(sysUserBo);
        return WxPayNotifyResponse.success("success");
    }

    /**
     * 将分转换为元，并保留精度。
     *
     * @param cents 分的金额，类型为Integer
     * @return 转换后的元金额，类型为double
     */
    public static double convertCentsToYuan(Integer cents) {
        // 处理空输入
        if (cents == null) {
            throw new IllegalArgumentException("输入的分金额不能为空");
        }

        // 100分 = 1元
        BigDecimal centsBigDecimal = new BigDecimal(cents);
        BigDecimal yuan = centsBigDecimal.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
        // 转换为double并返回
        return yuan.doubleValue();
    }

    /**
     * 获取订单信息
     *
     */
    @PostMapping("/orderInfo")
    public R<PaymentOrdersVo> orderInfo(@RequestBody  OrderRequest orderRequest) {
        if(StringUtils.isEmpty(orderRequest.getOrderNo())){
            throw new BaseException("订单号不能为空！");
        }
        PaymentOrdersBo paymentOrdersBo = new PaymentOrdersBo();
        paymentOrdersBo.setOrderNo(orderRequest.getOrderNo());
        List<PaymentOrdersVo> paymentOrdersList = paymentOrdersService.queryList(paymentOrdersBo);
        if (CollectionUtil.isEmpty(paymentOrdersList)){
            throw new BaseException("订单不存在！");
        }
        PaymentOrdersVo paymentOrdersVo = paymentOrdersList.get(0);
        return R.ok(paymentOrdersVo);
    }

    // 获取支付链接
//    static {
//        Stripe.apiKey = "sk_test_51PMMj2KcfX4oNioqXkoKpScTsgmR55xQki2tg8MEZJYc0gjhYV85t2FzDasE06eqZb0sqyYhOp3UXhcGGQLWI4A9008aq8SOnb";
//    }

    /**
     *   去支付
     * 1、创建产品
     * 2、设置价格
     * 3、创建支付信息 得到url
     * @return
     */
    @PostMapping("/stripePay")
    public String pay(@RequestBody OrderRequest orderRequest) throws StripeException {

        String enabled = configService.getConfigValue("stripe", "enabled");
        if(!Boolean.parseBoolean(enabled)){
            String prompt = configService.getConfigValue("stripe", "prompt");
            throw new BaseException(prompt);
        }

        // 获取支付链接
        Stripe.apiKey = configService.getConfigValue("stripe", "key");

        // 获取金额字符串并解析为 double
        double moneyDouble = Double.parseDouble(orderRequest.getMoney());

        // 将金额转换为以分为单位的整数
        int randMoney = (int) (moneyDouble * 100);

        Map<String, Object> params = new HashMap<>();
        params.put("name", orderRequest.getName());
        Product product = Product.create(params);

        Map<String, Object> recurring = new HashMap<>();
        recurring.put("interval", "month");
        Map<String, Object> params2 = new HashMap<>();
        params2.put("unit_amount", randMoney);
        params2.put("currency", "usd");
        params2.put("recurring", recurring);
        params2.put("product", product.getId());
        Price price = Price.create(params2);

        // 创建支付订单
        PaymentOrdersBo payOrder = paymentOrdersService.createPayOrder(orderRequest);

        //创建支付信息 得到url
        SessionCreateParams params3 = SessionCreateParams.builder()
            .setMode(SessionCreateParams.Mode.SUBSCRIPTION)
            .setSuccessUrl(configService.getConfigValue("stripe", "success"))
            .setCancelUrl(configService.getConfigValue("stripe", "cancel"))
            .addLineItem(
                SessionCreateParams.LineItem.builder()
                    .setQuantity(1L)
                    .setPrice(price.getId())
                    .build()).putMetadata("orderId", payOrder.getOrderNo())
            .build();
        Session session = Session.create(params3);
        return session.getUrl();
    }

    /**
     *  支付回调
     *
     */
    @PostMapping("/stripe_events")
    public R<String> stripeEvent(HttpServletRequest request) {
        try {
            String endpointSecret = configService.getConfigValue("stripe", "secret");//webhook秘钥签名
            InputStream inputStream = request.getInputStream();
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024*4];
            int n = 0;
            while (-1 != (n = inputStream.read(buffer))) {
                output.write(buffer, 0, n);
            }
            byte[] bytes = output.toByteArray();
            String payload = new String(bytes, "UTF-8");
            String sigHeader = request.getHeader("Stripe-Signature");
            Event event = Webhook.constructEvent(payload, sigHeader, endpointSecret);//验签，并获取事件
            if("checkout.session.completed".equals(event.getType())){
                // 解析 JSON 字符串为 JSONObject
                JSONObject jsonObject = JSONUtil.parseObj(event);
                // 获取 metadata 对象
                JSONObject metadata = jsonObject.getJSONObject("data")
                    .getJSONObject("object")
                    .getJSONObject("metadata");

                OrderRequest orderRequest = new OrderRequest();
                orderRequest.setPayType("stripe");
                orderRequest.setOrderNo(metadata.getStr("orderId"));
                paymentOrdersService.updatePayOrder(orderRequest);
            }
        } catch (Exception e) {
            System.out.println("stripe异步通知（webhook事件）"+e);
        }
        return R.ok();
    }

    public String getKey(String key) {
        return configService.getConfigValue("weixin", key);
    }

}

