package com.cyksj.service.stripe;

import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
import com.cyksj.common.util.Jsons;
import com.cyksj.enums.GatewayApiCode;
import com.cyksj.mapper.GoodsDonSkuMapper;
import com.cyksj.mapper.StripePayConfigMapper;
import com.cyksj.model.entity.GoodsDonSku;
import com.cyksj.model.entity.OrderDon;
import com.cyksj.model.entity.StripePayConfig;
import com.cyksj.model.request.OrderRefundReq;
import com.cyksj.service.order.OrderDonService;
import com.stripe.Stripe;
import com.stripe.exception.StripeException;
import com.stripe.model.*;
import com.stripe.model.checkout.Session;
import com.stripe.param.RefundCreateParams;
import com.stripe.param.checkout.SessionCreateParams;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;

/**
 * @author zwhui
 * @date 2021/6/21 11:03 上午
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class StripeService {

    private static final GlobalThreadPoolTaskExecutor THREAD_POOL = GlobalThreadPoolTaskExecutor.getInstance();



    private final OrderDonService orderDonService;

    private final StripePayConfigMapper stripePayConfigMapper;

    private final GoodsDonSkuMapper skuMapper;



    public String checkOutStripe(Long orderId,String successUrl) throws Exception{
        OrderDon orderDon = orderDonService.getById(orderId);
        if (orderDon.getGoodsId() == 26) {
            GoodsDonSku sku = skuMapper.selectById(orderDon.getSkuId());
            if (!sku.getIsMirror()) {
                throw BusinessRuntimeException.getInstance("订单异常..");
            }
        }
        BigDecimal money = orderDon.getMoney();
        orderDon.setType(OrderDon.Type.STRIPE);
        orderDonService.updateById(orderDon);
        HashMap<String, String> responseData = new HashMap<String, String>();
        if (OrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"The donation has been paid");
        }
        StripePayConfig stripePayConfig = stripePayConfigMapper.selectById(1);
        try {
            Stripe.apiKey = stripePayConfig.getApiKey();
            SessionCreateParams params =
                    SessionCreateParams.builder()
                            .setMode(SessionCreateParams.Mode.PAYMENT)
                            .setSuccessUrl(successUrl)
                            .setCancelUrl(stripePayConfig.getCancelUrl())
                            .setClientReferenceId(orderDon.getId().toString())
                            .addLineItem(
                                    SessionCreateParams.LineItem.builder()
                                            .setQuantity(1L)
                                            .setPriceData(
                                                    SessionCreateParams.LineItem.PriceData.builder()
                                                            .setCurrency("USD")
                                                            .setUnitAmount(money.longValue())
                                                            .setProductData(
                                                                    SessionCreateParams.LineItem.PriceData.ProductData.builder()
                                                                            .setName("donation")
                                                                            .setDescription("donation")
                                                                            .build()).build()).build()).build();
            Session session = Session.create(params);
            responseData.put("id", session.getId());
        } catch (StripeException e) {
            e.printStackTrace();
        }
        return Jsons.toJson(responseData);
    }


    public void fulfillOrder(Session session) throws Exception{
        if (null == session){
            log.error("支付回调失败");
            return;
        }
        String orderId = session.getClientReferenceId();
        if (!"paid".equals(session.getPaymentStatus())){
            return;
        }
        OrderDon orderDon = orderDonService.getById(orderId);
        if (orderDon == null){
            return;
        }
        if (orderDon.getStatus().equals(OrderDon.Status.hasPayment)) {
            log.info("已回调，重复修改状态 id:{}", orderDon.getId());
            return;
        }
        String paymentIntentId = session.getPaymentIntent();
        PaymentIntent paymentIntent = PaymentIntent.retrieve(paymentIntentId);
        String chargeId = paymentIntent.getLatestCharge();
        BalanceTransaction balanceTransaction = BalanceTransaction.retrieve(Charge.retrieve(chargeId).getBalanceTransaction());
        //更新订单支付状态

        orderDon.setTransactionId(chargeId);
        orderDon.setAbroadTransactionFee(BigDecimal.valueOf(balanceTransaction.getFee()));
        orderDon.setPayTime(new Date());
        orderDonService.orderNotify(orderDon);
        log.info("支付成功订单："+orderId+",支付交易号："+chargeId);
        log.info("支付成功订单："+orderId+",返回参数："+Jsons.toJson(session));

    }


    public Boolean refund(OrderRefundReq refundReq, OrderDon orderDon) throws StripeException {
        BigDecimal net = refundReq.getRefundMoney().divide(new BigDecimal(7),2, RoundingMode.HALF_DOWN).subtract(orderDon.getAbroadTransactionFee());
        log.info("stripe return order:{},money:{}",orderDon.getId(),net);
        StripePayConfig stripePayConfig = stripePayConfigMapper.selectById(1);
        Stripe.apiKey = stripePayConfig.getApiKey();
        RefundCreateParams params =
                RefundCreateParams.builder()
                        .setCharge(orderDon.getTransactionId())
                        .setAmount(net.longValue())
                        .build();
        Refund refund = Refund.create(params);
        log.info("stripe return refund:{}",refund);
        if ("succeeded".equals(refund.getStatus())){
            orderDon.setStatus(OrderDon.Status.refund);
            orderDonService.updateById(orderDon);
            return true;
        }
        return false;
    }

}
