package com.cyksj.web.controller.payment;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONObject;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cyksj.common.constant.Constant;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.util.IoKit;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.common.util.Xmls;
import com.cyksj.config.zfb.BaseZfbConfig;
import com.cyksj.config.zfb.ZfbProductCode;
import com.cyksj.dto.Result;
import com.cyksj.enums.GatewayApiCode;
import com.cyksj.enums.GatewayResponse;
import com.cyksj.mapper.*;
import com.cyksj.mapper.channel.UserPayEquipmentAvailableBenefitsMapper;
import com.cyksj.mapper.market.task.UserBindDetailMapper;
import com.cyksj.model.dto.AliPayParams;
import com.cyksj.model.dto.H5JsPayParams;
import com.cyksj.model.entity.*;
import com.cyksj.model.manage.views.*;
import com.cyksj.model.request.AlipayOrderDonReq;
import com.cyksj.model.request.OrderPayRequest;
import com.cyksj.model.response.AliPayTradeStatus;
import com.cyksj.model.views.GoodsDonSkuView;
import com.cyksj.model.views.LumaUserView;
import com.cyksj.model.views.MidjourneyUserView;
import com.cyksj.model.views.OrderCommentView;
import com.cyksj.service.luma.LumaService;
import com.cyksj.service.midjourney.MidjourneyAccountService;
import com.cyksj.service.order.OrderDonService;
import com.cyksj.service.paypal.PayPalService;
import com.cyksj.service.paypal.PaypalPayConfigService;
import com.cyksj.service.paypal.PaypalV2Service;
import com.cyksj.service.stripe.StripePayConfigService;
import com.cyksj.service.stripe.StripeService;
import com.cyksj.service.translations.BackInfoTranslationsFrontService;
import com.cyksj.service.user.UserBindRelationService;
import com.cyksj.web.util.StpUserUtil;
import com.ejlchina.searcher.BeanSearcher;
import com.ejlchina.searcher.SearchResult;
import com.ejlchina.searcher.param.Operator;
import com.ejlchina.searcher.util.MapBuilder;
import com.ejlchina.searcher.util.MapUtils;
import com.google.gson.JsonSyntaxException;
import com.paypal.api.payments.Links;
import com.paypal.api.payments.Payment;
import com.stripe.exception.SignatureVerificationException;
import com.stripe.model.*;
import com.stripe.model.checkout.Session;
import com.stripe.net.Webhook;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chan
 * @date 2021/10/11 下午3:50
 */
@Slf4j
@RequestMapping("/applets/order")
@RestController
public class OrderController {

    @Autowired
    private OrderDonService orderDonService;

    @Resource
    private BeanSearcher beanSearcher;

    @Resource
    private HttpServletRequest request;

    @Resource
    private HttpServletResponse response;

    @Autowired
    private UserBindDetailMapper userBindDetailMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserPayEquipmentAvailableBenefitsMapper availableBenefitsMapper;

    @Autowired
    private OrderDonWaybillMapper waybillMapper;

    @Autowired
    private UserBindRelationService UserBindRelationService;

    @Autowired
    private PaypalPayConfigService paypalPayConfigService;

    @Autowired
    private PayPalService payPalService;

    @Autowired
    private PaypalV2Service paypalV2Service;

    @Autowired
    private StripePayConfigService stripePayConfigService;

    @Autowired
    private StripeService stripeService;

    @Autowired
    private MidjourneyAccountService midjourneyAccountService;

    @Autowired
    private GoodsDonSkuMapper skuMapper;

    @Autowired
    private OrderDonRenewRecordMapper orderDonRenewRecordMapper;

    @Autowired
    private LumaService lumaService;

    @Autowired
    private UserBindRelationService userBindRelationService;

    @Autowired
    private GroupsRelationMapper relationMapper;

    @Autowired
    private BackInfoTranslationsFrontService backInfoTranslationsFrontService;

    @Autowired
    private UpgradePackageMapper upgradePackageMapper;

    /**
     * 支付
     * @author chan
     * @date 2021-10-11 下午4:56
     */
    @PostMapping("/post/submit")
    public Result<OrderDon> submit(@Validated @RequestBody OrderPayRequest payRequest, HttpServletResponse response) throws Exception {
        final String methodName = "吊起微信支付";
        Long userId = StpUserUtil.getUserIdAfterLogin();
        payRequest.setUserId(userId);
        log.info("{}[start] params:{}", methodName, payRequest);

        String ip = ServletUtil.getClientIP(request);
        String address = StringUtil.getNewInternalAddressByIp(ip);
        //address.contains("柬埔寨") ||
        if (address != null && address.contains("缅甸")) {
            throw BusinessRuntimeException.getInstance("用户异常");
        }
        payRequest.setIp(ip);

        //非推广 免登录
        if (payRequest.getPopularizeId() == null && payRequest.getIsNoLogin()) {
            throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID);
        }

        OrderDon orderDon = orderDonService.submit(payRequest);

        //无登录订单 预留手机号免登录
        if (userId == null && payRequest.getPopularizeId() != null && StrUtil.isNotBlank(payRequest.getPhone())) {
            User user = userMapper.selectById(orderDon.getUserId());
            //写回登陆信息
            StpUserUtil.login(user.getId());
            response.addHeader("satoken-user", StpUserUtil.getTokenValue());

            Cookie cookie = new Cookie("satoken-user", StpUserUtil.getTokenValue());
            cookie.setMaxAge(31536000);
            cookie.setPath("/");
            response.addCookie(cookie);
            JSONObject jsonObject = new JSONObject();
            jsonObject.putOpt("showId", user.getShowId());
            jsonObject.putOpt("nickName", user.getNickname());

            Cookie userInfo = new Cookie("info", Base64.encode(jsonObject.toString()));
            cookie.setMaxAge(31536000);
            cookie.setPath("/");
            response.addCookie(userInfo);
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
    }

    /**
     * 套餐提交支付
     */
    @PostMapping("/post/upgrade/submit")
    public Result<UpgradeOrderDon> submitUpgrade(@Validated @RequestBody OrderPayRequest payRequest) throws Exception {
        final String methodName = "套餐吊起支付";
        //无需免登录
        Long userId = StpUserUtil.getLoginIdAsLong();
        payRequest.setUserId(userId);
        log.info("{}[start] params:{}", methodName, payRequest);

        String ip = ServletUtil.getClientIP(request);

        payRequest.setIp(ip);

        UpgradeOrderDon orderDon = orderDonService.submitUpgrade(payRequest);

        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
    }

    /**
     * 0元订单确认
     */
    @PostMapping("/post/order/status/{orderId}")
    @Deprecated
    public Result<String> zeroOrderConfirm(@PathVariable Long orderId) {
        orderDonService.zeroOrderConfirm(orderId);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    /**
     * 套餐0元订单确认
     */
    @PostMapping("/post/upgrade/order/status/{orderId}")
    @Deprecated
    public Result<String> zeroUpgradeOrderConfirm(@PathVariable Long orderId) {
        orderDonService.zeroUpgradeOrderConfirm(orderId);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }


    /**
     * 支付
     * @author chan
     * @date 2021-10-11 下午4:56
     */
    @PostMapping("/post/pay/{orderId}")
    public Result<H5JsPayParams> pay(@PathVariable Long orderId) throws Exception {
        final String methodName = "吊起微信支付";
        log.info("{}[start] orderId:{}",methodName,orderId);

        H5JsPayParams payParams = orderDonService.pay(orderId, "JSAPI");

        return GatewayResponse.SUCCESS.newBuilder().toResult(payParams);
    }

    /**
     * 调用生成订单之后
     * 微信网页预下单获取交易链接生成二维码
     */
    @PostMapping("/get/wx/orderQrCode/{orderId}")
    public Result<String> createUnifiedOrderQr(@PathVariable Long orderId) throws Exception {
        //预下单
        H5JsPayParams payParams = orderDonService.wxPcWebPay(orderId, "NATIVE");
        //获取二维码链接code_url
        String codeUrl = payParams.getCodeUrl();
        QrConfig qrConfig = new QrConfig(300, 300);
        return GatewayResponse.SUCCESS.newBuilder().toResult(QrCodeUtil.generateAsBase64(codeUrl, qrConfig, "png"));
    }

    /**
     * 微信支付回调
     * @author chan
     * @date 2021-10-11 下午5:42
     */
    @PostMapping("/prepay/notify")
    public void prepayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String xml = IoKit.toString(request.getInputStream());
        log.info("=== 微信支付成功的回调: \n {}", xml);

        // 解析xml
        Map<String, String> map = Xmls.toMap(xml);

        // 支付失败
        if (!StringUtils.equals("SUCCESS", map.get("return_code")) || !StringUtils.equals("SUCCESS", map.get("result_code"))) {
            throw BusinessRuntimeException.getInstance("支付失败.");
        }

        orderDonService.prepayNotify(map);
        String toWeChatXmlResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

        OutputStream out = null;
        try {
            out = response.getOutputStream();
            IoKit.write(toWeChatXmlResponse, out);
        } catch (Exception e) {
            throw BusinessRuntimeException.getInstance(e,e.getMessage());
        } finally {
            IoKit.close(out);
        }
    }

    /**
     * pc端微信支付回调
     * @author chan
     * @date 2021-10-11 下午5:42
     */
    @PostMapping("/pc/prepay/notify")
    public void pcPrepayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {

        String xml = IoKit.toString(request.getInputStream());
        log.info("=== 微信pc端分发支付成功的回调: \n {}", xml);

        // 解析xml
        Map<String, String> map = Xmls.toMap(xml);

        // 支付失败
        if (!StringUtils.equals("SUCCESS", map.get("return_code")) || !StringUtils.equals("SUCCESS", map.get("result_code"))) {
            throw BusinessRuntimeException.getInstance("支付失败.");
        }

        orderDonService.prepayNotify(map);
        log.info("pc端微信支付回调修改订单状态成功");
        String toWeChatXmlResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";

        OutputStream out = null;
        try {
            out = response.getOutputStream();
            IoKit.write(toWeChatXmlResponse, out);
        } catch (Exception e) {
            throw BusinessRuntimeException.getInstance(e,e.getMessage());
        } finally {
            IoKit.close(out);
        }
    }

	/**
     * 支付宝表单支付
	 * 支付宝调起支付宝表单
     * 手机支付
	 */
	@PostMapping("/post/pay/zfb")
    public Result<String> aliPay(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
        AliPayParams aliPayParams = orderDonService.aliPay(re, ZfbProductCode.MOBILE_WEB.getProductCode(), null);
//        response.setContentType("text/html;charset=" + BaseZfbConfig.charset);
        //直接将完整的表单html输出到页面
//        response.getWriter().write(aliPayParams.getBody());
//        response.getWriter().flush();
//        response.getWriter().close();
        return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
	}

    /**
     * 套餐支付宝支付
     */
    @PostMapping("/post/pay/upgrade/zfb")
    public Result<String> aliPayUpgrade(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
        AliPayParams aliPayParams = orderDonService.aliPayUpgrade(re, ZfbProductCode.MOBILE_WEB.getProductCode(),null);
        return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
    }


    /**
     * 支付宝web支付
     * pc网页内嵌二维码
     */
    @PostMapping("/get/zfb/orderQrCode")
    public Result<String> zfbWebQrCode(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
        AliPayParams aliPayParams = orderDonService.aliPay(re, ZfbProductCode.PC_WEB.getProductCode(), BaseZfbConfig.qrPayMode_orderCode);
//        response.setContentType("text/html;charset=" + BaseZfbConfig.charset);
        //直接将完整的表单html输出到页面
//        response.getWriter().write(aliPayParams.getBody());
//        response.getWriter().flush();
//        response.getWriter().close();
        return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
    }

    /**
     * 支付宝web支付
     * pc网页内嵌二维码
     */
    @PostMapping("/get/zfb/upgrade/orderQrCode")
    public Result<String> zfbWebQrCodeUpgrade(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
        AliPayParams aliPayParams = orderDonService.aliPayUpgrade(re, ZfbProductCode.PC_WEB.getProductCode(), BaseZfbConfig.qrPayMode_orderCode);
//        response.setContentType("text/html;charset=" + BaseZfbConfig.charset);
        //直接将完整的表单html输出到页面
//        response.getWriter().write(aliPayParams.getBody());
//        response.getWriter().flush();
//        response.getWriter().close();
        return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
    }

    /**
     * 支付宝web支付
     * pc网页跳转二维码
     */
    @PostMapping("/get/zfb/orderRedirect")
    public Result<String> zfbWebRedirectQrCode(@RequestBody @Validated AlipayOrderDonReq re) throws Exception {
        AliPayParams aliPayParams = orderDonService.aliPay(re, ZfbProductCode.PC_WEB.getProductCode(), BaseZfbConfig.qrPayMode_redirect);
        return GatewayResponse.SUCCESS.newBuilder().toResult(aliPayParams.getBody());
    }

    /**
	 * 支付宝回调
	 */
	@PostMapping("/prepay/zfb/notify")
	public String aliPayNotify(HttpServletRequest request) throws Exception {
		////将异步通知中收到的所有参数都存放到map中
		Map<String, String> params = convertRequestParamsToMap(request);
		String tradeStatus = params.get("trade_status");
        String orderNo = params.get("out_trade_no");
        if (AliPayTradeStatus.TRADE_SUCCESS.getStatus().equals(tradeStatus) || AliPayTradeStatus.TRADE_FINISHED.getStatus().equals(tradeStatus)) {
            log.info("支付宝商家订单号orderNo:{},status:{}开始回调", orderNo, tradeStatus);
            orderDonService.aliPayNotify(params);
		} else {
			log.error("支付宝回调 error 状态不符合 成功或完成 status:{} ,orderNo:{}", tradeStatus, orderNo);
			return "fail";
		}
		return "success";
	}

    /**
     * 支付宝套餐包回调
     */
    @PostMapping("/prepay/zfb/upgrade/notify")
    public String aliPayUpgradeNotify(HttpServletRequest request) throws Exception {
        ////将异步通知中收到的所有参数都存放到map中
        Map<String, String> params = convertRequestParamsToMap(request);
        String tradeStatus = params.get("trade_status");
        String orderNo = params.get("out_trade_no");
        if (AliPayTradeStatus.TRADE_SUCCESS.getStatus().equals(tradeStatus) || AliPayTradeStatus.TRADE_FINISHED.getStatus().equals(tradeStatus)) {
            log.info("支付宝商家订单号orderNo:{},status:{}开始回调", orderNo, tradeStatus);
            orderDonService.aliPayUpgradeNotify(params);
        } else {
            log.error("支付宝回调 error 状态不符合 成功或完成 status:{} ,orderNo:{}", tradeStatus, orderNo);
            return "fail";
        }
        return "success";
    }


    /**
     * 支付宝转账
     */
//    @PostMapping("/zfb/transfer/accounts")
//    @NoSubmit
//    public Result<String> zfbTransferAccounts(@RequestBody @Validated TransferAccountsReq transferAccountsReq) throws Exception {
//        long userId = StpUserUtil.getLoginIdAsLong();
//        transferAccountsReq.setUserId(userId);
//        orderDonService.transferAccounts(transferAccountsReq);
//        return GatewayResponse.SUCCESS.newBuilder().toResult("转账成功");
//    }

    /**
     * 查询支付宝转账状态
     */
    @GetMapping("/zfb/transfer/status")
    public Result<String> getTransFerStatusByTid(String translationId) throws Exception {
        AlipayFundTransCommonQueryResponse response = orderDonService.getTransFerStatusByTid(translationId);
        if (response.isSuccess()) {
            if (!Constant.SUCCESS.equals(response.getStatus())) {
                return GatewayResponse.SUCCESS.newBuilder().toResult(response.getFailReason());
            }
            return GatewayResponse.SUCCESS.newBuilder().toResult("该红包已转账成功");
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(response.getSubMsg());
    }

    /**
     * 获取订单列表
     */
    @GetMapping("/get")
    public Result<SearchResult<OrderDonView>> list(String customId){
        long userId = StpUserUtil.getLoginIdAsLong();
        MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
        //Long yhsId = yhShopFrontService.getYhsIdByCustomId(customId);
        List<Long> userIds = UserBindRelationService.getRelationUserIdList(userId, null);
        SearchResult<OrderDonView> search = beanSearcher.search(OrderDonView.class, builder
                //.field(OrderDonView::getYhsId, yhsId)
                .field(OrderDonView::getUserId, userIds).op(Operator.InList)
                .orderBy(OrderDonView::getId).desc()
                .build());
        String lang = request.getHeader("lang");
        search.getDataList().forEach(data->{
            Integer count = availableBenefitsMapper.selectCount(Wrappers.lambdaQuery(UserPayEquipmentAvailableBenefits.class)
                    .eq(UserPayEquipmentAvailableBenefits::getRealOrderId, data.getId())
                    .eq(UserPayEquipmentAvailableBenefits::getDeleted, true));
            data.setIsHasBenefits(count > 0 ? true : false);
            if (data.getIsBenefits() != null && data.getIsBenefits()) {
                String benefitsList = data.getBenefitsList();
                if (StrUtil.isNotBlank(benefitsList)) {
                    try {
                        List<Long> skuIds = Jsons.parseList(benefitsList, GoodsDonSku.class).stream().map(GoodsDonSku::getId).collect(Collectors.toList());
                        if (CollUtil.isNotEmpty(skuIds)) {
                            data.setBenefitsViews(beanSearcher.searchAll(GoodsDonSkuView.class, MapUtils.builder().field(GoodsDonSkuView::getSkuId, skuIds).op(Operator.InList).build()));
                        }
                    } catch (Exception e) {

                    }
                }
            }
            if (data.getRelationId() == 0) {
                if (data.getGoodsDonType() == null || data.getGoodsDonType() != 5) {
                    data.setWaybill(waybillMapper.selectOne(Wrappers.lambdaQuery(OrderDonWaybill.class).eq(OrderDonWaybill::getOrderId, data.getId()).last("limit 1")));
                }
            }
            if (data.getOrderType() == 2) {
                OrderDonRenewRecord orderDonRenewRecord = orderDonRenewRecordMapper.selectOne(Wrappers.lambdaQuery(OrderDonRenewRecord.class)
                        .eq(OrderDonRenewRecord::getOrderId, data.getId())
                        .last("limit 1"));
                data.setIsRenewPackage(orderDonRenewRecord != null ? true : false);
            }
            if (lang != null && !StrUtil.equals(UserPageLanguage.Language.zh_CN.name(), lang)) {
                String languagePayTitle = backInfoTranslationsFrontService.getLanguagePayTitle(data.getGoodsId(), data.getSkuId(), lang);
                if (StrUtil.isNotBlank(languagePayTitle)) {
                    data.setPayTitle(languagePayTitle);
                }
                String languageSpecVal = backInfoTranslationsFrontService.getLanguageSpecVal(data.getSkuId(), lang);
                if (StrUtil.isNotBlank(languageSpecVal)) {
                    data.setSpecVal(languageSpecVal);
                }
            }
        });
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }


    /**
     * 获取订单详情
     */
    @GetMapping("/get/{orderId}")
    public Result<OrderDetailView> detail(@PathVariable Long orderId, Boolean isNoLogin) {
        Long userId = StpUserUtil.getUserIdAfterLogin();

        OrderDetailView detail = orderDonService.getDetail(orderId, userId);

        return GatewayResponse.SUCCESS.newBuilder().toResult(detail);
    }

    /**
     * 取消订单
     */
    @GetMapping("/get/close/{orderId}")
    public Result<String> closeOrder(@PathVariable Long orderId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        orderDonService.closeOrder(orderId, userId);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    @GetMapping("/get/order/status/{orderId}")
    public Result<OrderDonSearchView> getOrderStatusById(@PathVariable Long orderId) {
        if (orderId < 1) {
            throw BusinessRuntimeException.getInstance("订单不存在");
        }
        OrderDonSearchView orderDonView = beanSearcher.searchFirst(OrderDonSearchView.class, MapUtils.builder()
                .put("orderId", String.format("where id = %s", orderId))
                .build());
        if (orderDonView == null) {
            throw BusinessRuntimeException.getInstance("订单不存在");
        }
        if (orderDonView.getOrderType() == 2 && orderDonView.getIsMirror()) {
            Optional.ofNullable(orderDonRenewRecordMapper.selectOne(Wrappers.lambdaQuery(OrderDonRenewRecord.class)
                            .eq(OrderDonRenewRecord::getOrderId, orderId)
                            .last("limit 1")))
                    .ifPresent(renew -> {
                        GoodsDonSku sku = skuMapper.selectById(renew.getOriSkuId());
                        orderDonView.setOriSpecVal(sku.getSpecVal());
                    });
        }
        String lang = request.getHeader("lang");
        if (lang != null && !StrUtil.equals(UserPageLanguage.Language.zh_CN.name(), lang)) {
            String languagePayTitle = backInfoTranslationsFrontService.getLanguagePayTitle(orderDonView.getGoodsId(), orderDonView.getSkuId(), lang);
            if (StrUtil.isNotBlank(languagePayTitle)) {
                orderDonView.setPayTitle(languagePayTitle);
            }
            String languageSpecVal = backInfoTranslationsFrontService.getLanguageSpecVal(orderDonView.getSkuId(), lang);
            if (StrUtil.isNotBlank(languageSpecVal)) {
                orderDonView.setSpecVal(languageSpecVal);
            }
            String languagePayDesc = backInfoTranslationsFrontService.getLanguagePayDesc(orderDonView.getGoodsId(), lang);
            if (StrUtil.isNotBlank(languagePayDesc)) {
                orderDonView.setPayDesc(languagePayDesc);
            }
        }

        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDonView);
    }

    /**
     * 查询套餐订单支付状态
     */
    @GetMapping("/get/upgrade/order/status/{orderId}")
    public Result<UpgradeOrderDonSearchView> getUpgradeOrderStatusById(@PathVariable Long orderId) {
        if (orderId < 1) {
            throw BusinessRuntimeException.getInstance("订单不存在");
        }
        UpgradeOrderDonSearchView orderDonView = beanSearcher.searchFirst(UpgradeOrderDonSearchView.class, MapUtils.builder()
                .put("orderId", String.format("where id = %s", orderId))
                .build());
        if (orderDonView == null) {
            throw BusinessRuntimeException.getInstance("订单不存在");
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDonView);
    }

    @PostMapping("/get/noLogin/order")
    public Result<List<OrderDonView>> getSearchResult(@RequestBody List<Long> orderIds) {
        List<OrderDonView> orderDonViews = beanSearcher.searchAll(OrderDonView.class, MapUtils.builder()
                .field(OrderDonView::getId, orderIds).op(Operator.InList)
                .orderBy(OrderDonView::getId).desc().build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDonViews);
    }

    /**
     * 平台评论列表
     */
    @GetMapping("/get/orderComment")
    public Result<SearchResult<OrderCommentView>> getOrderCommentView() {
        SearchResult<OrderCommentView> search = beanSearcher.search(OrderCommentView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 电脑端手机号注册 购买车票是否绑定了微信用户
     */
    @GetMapping("/isBindWx")
    public Result<Boolean> isBindWx() {
        Long userId = StpUserUtil.getLoginIdAsLong();
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID);
        }
        if (StrUtil.isEmpty(user.getLoginPhone())) {
            return GatewayResponse.SUCCESS.newBuilder().toResult(true);
        }
        UserBindDetail userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
                .eq(UserBindDetail::getPhone, user.getLoginPhone()));
        if (userBindDetail == null || userBindDetail.getUserId() == null) {
            return GatewayResponse.SUCCESS.newBuilder().toResult(false);
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(true);
    }

    /**
     * 是否绑定了邮箱或手机号
     */
    @GetMapping("/isBind")
    public Result<Boolean> isBindPhoneOrEmail(@RequestParam(defaultValue = "1") Boolean fip) {
        Long userId = StpUserUtil.getLoginIdAsLong();
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw BusinessRuntimeException.getGatewayApiCode(GatewayApiCode.CMS_TOKEN_VALID);
        }
        //手机号注册 无需绑定
        if (StrUtil.isNotEmpty(user.getLoginPhone())) {
            return GatewayResponse.SUCCESS.newBuilder().toResult(true);
        }
        //是否绑定了手机号
        UserBindDetail userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
                .eq(StrUtil.isNotEmpty(user.getUnionid()), UserBindDetail::getUserId, userId)
                .eq(StrUtil.isNotEmpty(user.getEmail()), UserBindDetail::getEmailUserId, userId).last("limit 1"));
        if (fip) {
            //台湾ip
            String address = StringUtil.getNewInternalAddressByIp(ServletUtil.getClientIP(request));
            if (address.contains("台湾") || !address.contains("中国")) {
                return GatewayResponse.SUCCESS.newBuilder().toResult(true);
            }
        }
        if (userBindDetail == null || StrUtil.isEmpty(userBindDetail.getPhone())) {
            return GatewayResponse.SUCCESS.newBuilder().toResult(false);
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(true);
    }

    /**
     * 获取支付宝回调参数
     */
    private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<>();
        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;
            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.substring(1));
            } else {
                retMap.put(name, "");
            }
        }
        return retMap;
    }


    /**
     * paypal支付
     */
    @PostMapping("/paypal/pay/{orderId}")
    public Result<String> paypalPay(@PathVariable Long orderId,String successUrl) throws Exception {
        final String methodName = "paypal支付";
        log.info("{}[start] orderId:{}",methodName,orderId);

        Payment payment = payPalService.createPayment(orderId,successUrl);
        String payUrl = "/";
        log.info("paypal links:{}",payment.getLinks());
        for(Links links : payment.getLinks()){
            if("approval_url".equals(links.getRel())){
                // 客户付款登陆地址
                String payPalUrl = links.getHref();
                log.info("PayPal调起支付成功[end]：{}", orderId);
                payUrl = payPalUrl;
            }
        }

        log.info("paypal支付成功[end]");

        return GatewayResponse.SUCCESS.newBuilder().toResult(payUrl);
    }


    /**
     * stripe支付
     */
    @PostMapping("/stripe/pay/{orderId}")
    public Result<String> stripePay(@PathVariable Long orderId,String successUrl) throws Exception {
        final String methodName = "stripe支付";
        log.info("{}[start] orderId:{}",methodName,orderId);
        String payUrl = stripeService.checkOutStripe(orderId,successUrl);

        log.info("Stripe支付成功 result:{}[end]",payUrl);
        return GatewayResponse.SUCCESS.newBuilder().toResult(payUrl);
    }


    /**
     * paypal支付回调
     */
    @RequestMapping(value = "/paypal/notify")
    public void successPay(@RequestParam(value = "paymentId") String paymentId, @RequestParam(value = "PayerID") String PayerID) throws Exception {
        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
        response.sendRedirect(payPalService.successPayment(paymentId,PayerID,paypalPayConfig));
    }


    /**
     * stripe支付回调
     */
    @PostMapping("/stripe/notify")
    @ResponseBody
    public String webhooks() throws Exception {
        InputStream inputStream = request.getInputStream();
        byte[] bytes = IoKit.toBytes(inputStream);
        String payload = new String(bytes, StandardCharsets.UTF_8);

        String sigHeader = request.getHeader("Stripe-Signature");
        StripePayConfig stripePayConfig = stripePayConfigService.getById(1);
        Event event = null;
        try {
            event = Webhook.constructEvent(
                    payload, sigHeader, stripePayConfig.getWebhookSecret()
            );
        } catch (JsonSyntaxException | SignatureVerificationException e) {
            response.setStatus(400);
            return "";
        }

        // Deserialize the nested object inside the event
        EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
        StripeObject stripeObject = null;
        if (dataObjectDeserializer.getObject().isPresent()) {
            stripeObject = dataObjectDeserializer.getObject().orElse(null);
        } else {
            // Deserialization failed, probably due to an API version mismatch.
            // Refer to the Javadoc documentation on `EventDataObjectDeserializer` for
            // instructions on how to handle this case, or return an error here.
        }

        // Handle the event
        switch (event.getType()) {
            case "payment_intent.succeeded":
                PaymentIntent paymentIntent = (PaymentIntent) stripeObject;
                response.setStatus(200);
                break;
            case "charge.succeeded":
                //使用token支付成功回调
                Charge charge = (Charge) stripeObject;
                //TODO 此时根据charge ID 查询出关联的订单并处理支付成功业务代码
                response.setStatus(200);
                break;
            case "checkout.session.completed":
                //使用checkout支付成功回调
                Session session = (Session) stripeObject;
                stripeService.fulfillOrder(session);
                response.setStatus(200);
                break;
            default:
                response.setStatus(400);
                return "";
        }
        response.setStatus(200);
        return "";
    }

    /**
     * 余额明细
     */
    @GetMapping("/get/balance/detail")
    public Result<SearchResult<UserBalanceSourceRecord>> getUserBalanceSourceRecord() {
        long userId = StpUserUtil.getLoginIdAsLong();
        SearchResult<UserBalanceSourceRecord> search = beanSearcher.search(UserBalanceSourceRecord.class, MapUtils.flatBuilder(request.getParameterMap())
                .field(UserBalanceSourceRecord::getUserId, userId)
                .field(UserBalanceSourceRecord::getDetail).op(Operator.NotNull)
                .orderBy(UserBalanceSourceRecord::getId).desc()
                .onlySelect(UserBalanceSourceRecord::getBalance, UserBalanceSourceRecord::getDetail, UserBalanceSourceRecord::getCreatedTime)
                .build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }


    /**
     * 获取套餐订单列表
     */
    @GetMapping("/upgrade/get")
    public Result<SearchResult<UpgradeOrderDonView>> listUpgradeOrder(){
        long userId = StpUserUtil.getLoginIdAsLong();
        MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
        List<Long> userIds = UserBindRelationService.getRelationUserIdList(userId, null);
        SearchResult<UpgradeOrderDonView> search = beanSearcher.search(UpgradeOrderDonView.class, builder
                .field(UpgradeOrderDonView::getUserId, userIds).op(Operator.InList)
                .orderBy(UpgradeOrderDonView::getId).desc()
                .build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }


    /**
     * 获取套餐订单详情
     */
    @GetMapping("/get/upgrade/{orderId}")
    public Result<UpgradeOrderDetailView> upgradeOrderDetail(@PathVariable Long orderId) {
        Long userId = StpUserUtil.getUserIdAfterLogin();

        UpgradeOrderDetailView detail = orderDonService.getUpgradeOrderDetail(orderId, userId);

        return GatewayResponse.SUCCESS.newBuilder().toResult(detail);
    }

    /**
     * 取消套餐订单
     */
    @GetMapping("/get/close/upgrade/{orderId}")
    public Result<String> closeUpgradeOrder(@PathVariable Long orderId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        orderDonService.closeUpgradeOrder(orderId, userId);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }

    /**
     * midjourney查询订单用户账号剩余次数
     */
    @GetMapping("/get/midjourney/user")
    public Result<MidjourneyUserView> getMidjourneyUser(Long relationId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        MidjourneyUserView search = beanSearcher.searchFirst(MidjourneyUserView.class, MapUtils.flatBuilder(request.getParameterMap())
                .field(MidjourneyUserView::getRelationId, relationId).build());
        if (search == null) {
            MidjourneyUser midjourneyUser = midjourneyAccountService.getMidjourneyUserToken(userId, relationId);
            search = new MidjourneyUserView();
            BeanUtils.copyProperties(midjourneyUser, search);
        } else {
            List<UpgradePackage> upgradePackages = upgradePackageMapper.selectList(Wrappers.lambdaQuery(UpgradePackage.class).apply("JSON_CONTAINS(sku_ids,'\"" + search.getSkuId() + "\"')").eq(UpgradePackage::getStatus, Boolean.TRUE).eq(UpgradePackage::getDeleted, Boolean.TRUE));
            if (CollectionUtil.isNotEmpty(upgradePackages)) {
                upgradePackages.stream().map(e -> e.getPrice().divide(BigDecimal.valueOf(e.getNum()), 2, RoundingMode.HALF_UP)).min(BigDecimal::compareTo).ifPresent(search::setMjPackagePrice);
            }
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 续费升级提交支付
     */
    @PostMapping("/post/renew/upgrade/submit")
    public Result<OrderDon> submitRenewUpgrade(@Validated @RequestBody OrderPayRequest payRequest) throws Exception {
        final String methodName = "续费升级吊起支付";
        //无需免登录
        Long userId = StpUserUtil.getLoginIdAsLong();
        payRequest.setUserId(userId);
        log.info("{}[start] params:{}", methodName, payRequest);

        String ip = ServletUtil.getClientIP(request);

        payRequest.setIp(ip);

        OrderDon orderDon = orderDonService.submitRenewUpgrade(payRequest);

        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
    }

    /**
     * 获取续费套餐订单列表
     */
    @GetMapping("/renew/get")
    public Result<SearchResult<RenewUpgradeOrderDonView>> listRenewOrders(){
        long userId = StpUserUtil.getLoginIdAsLong();
        MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
        List<Long> userIds = UserBindRelationService.getRelationUserIdList(userId, null);
        SearchResult<RenewUpgradeOrderDonView> search = beanSearcher.search(RenewUpgradeOrderDonView.class, builder
                .field(UpgradeOrderDonView::getUserId, userIds).op(Operator.InList)
                .orderBy(UpgradeOrderDonView::getId).desc()
                .build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * luma查询订单用户账号剩余次数
     */
    @GetMapping("/get/luma/user")
    public Result<LumaUserView> getLumaUser(Long relationId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        LumaUserView search = beanSearcher.searchFirst(LumaUserView.class, MapUtils.flatBuilder(request.getParameterMap())
                .field(LumaUserView::getUserId, userId).field(LumaUserView::getRelationId, relationId).build());
        if (search == null) {
            LumaUser lumaUser = lumaService.getLumaUser(userId, relationId);
            search = new LumaUserView();
            BeanUtils.copyProperties(lumaUser, search);
        }
        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
    }

    /**
     * 用户续费订单列表
     */
    @GetMapping("/get/renew/order")
    public Result<List<OrderDonView>> getRenewOrder(Long relationId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
        if (relationId != null) {
            builder.field(OrderDonView::getRelationId, relationId);
        }
        GroupsRelation relation = relationMapper.selectById(relationId);
        if (relation == null) {
            return GatewayResponse.SUCCESS.newBuilder().toResult();
        }
        List<Long> userIds = UserBindRelationService.getRelationUserIdList(userId, null);
        List<OrderDonView> list = beanSearcher.searchAll(OrderDonView.class, builder
                .field(OrderDonView::getUserId, userIds).op(Operator.InList)
                .field(OrderDonView::getOrderType, 2)
                .field(OrderDonView::getCreatedTime, relation.getStartTime()).op(Operator.GreaterThan)
                .orderBy(OrderDonView::getId).desc()
                .build());
        return GatewayResponse.SUCCESS.newBuilder().toResult(list);
    }

    /**
     * 用户权益预支付
     */
    @PostMapping("/user/benefits/submit")
    public Result<OrderDon> userBenefitsSubmit(@RequestBody OrderPayRequest orderPayRequest) throws Exception {
        long userId = StpUserUtil.getLoginIdAsLong();
        orderPayRequest.setUserId(userId);
        String ip = ServletUtil.getClientIP(request);
        orderPayRequest.setIp(ip);
        OrderDon orderDon = orderDonService.userBenefitsSubmit(orderPayRequest);
        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDon);
    }

    /**
     * 是否是实物可选多张车票权益记录
     */
    @GetMapping("/get/combo/optional/ticket/record/{orderId}")
    public Result<UserPayEquipmentAvailableBenefits> getComboOptionalTicketRecord(@PathVariable Long orderId) {
        long userId = StpUserUtil.getLoginIdAsLong();
        UserPayEquipmentAvailableBenefits userPayEquipmentAvailableBenefits = orderDonService.getComboOptionalTicketRecord(userId, orderId);
        return GatewayResponse.SUCCESS.newBuilder().toResult(userPayEquipmentAvailableBenefits);
    }

    /**
     * 是否购买过实物设备
     */
    @GetMapping("/has/paid/equipment")
    public Result<Boolean> hasPaidEquipment() {
        long userId = StpUserUtil.getLoginIdAsLong();
        List<Long> userIdList = userBindRelationService.getRelationUserIdList(userId, null);
        //是否买过设备
        Integer selectCount = orderDonService.count(Wrappers.lambdaQuery(OrderDon.class)
                .eq(OrderDon::getGoodsId, 15)
                .in(OrderDon::getUserId, userIdList)
                .notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
        return GatewayResponse.SUCCESS.newBuilder().toResult(selectCount > 0);
    }

    /**
     * paypalV2支付
     */
    @PostMapping("/paypalV2/pay/{orderId}")
    public Result<String> paypalV2Pay(@PathVariable Long orderId,String successUrl) throws Exception {
        final String methodName = "paypal支付";
        log.info("{}[start] orderId:{}",methodName,orderId);

        String payUrl = paypalV2Service.createPayment(orderId,successUrl);
        log.info("paypal links:{}",payUrl);
        log.info("paypal支付成功[end]");

        return GatewayResponse.SUCCESS.newBuilder().toResult(payUrl);
    }


    /**
     * paypal支付回调
     */
    @RequestMapping(value = "/paypalV2/notify")
    public void successV2Pay(@RequestParam(value = "token") String token, @RequestParam(value = "PayerID") String PayerID) throws Exception {
        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
        response.sendRedirect(paypalV2Service.successPayment(token,PayerID,paypalPayConfig));
    }

    /**
     * 领取GPT镜像天卡
     */
    @PostMapping("/receive/gptMirror/dayCard/{orderId}")
    public Result<String> receiveGptMirrorDayCard(@PathVariable Long orderId) throws Exception {
        orderDonService.receiveGptMirrorDayCard(orderId);
        return GatewayResponse.SUCCESS.newBuilder().toResult();
    }
}
