package com.cyksj.service.paypal;

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.model.entity.GoodsDonSku;
import com.cyksj.model.entity.OrderDon;
import com.cyksj.model.entity.PaypalPayConfig;
import com.cyksj.model.entity.VipOrderDon;
import com.cyksj.model.request.OrderRefundReq;
import com.cyksj.redis.RedisService;
import com.cyksj.service.order.OrderDonService;
import com.cyksj.service.order.VipOrderDonService;
import com.paypal.api.payments.Currency;
import com.paypal.api.payments.*;
import com.paypal.base.rest.APIContext;
import com.paypal.base.rest.OAuthTokenCredential;
import com.paypal.base.rest.PayPalRESTException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

/**
 * @author chan
 * @date 2021/3/26 11:03 上午
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class PayPalService{

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


    private final OrderDonService orderDonService;

    private final PaypalPayConfigService paypalPayConfigService;

    private final RedisService redisService;

    private final GoodsDonSkuMapper skuMapper;

    private final VipOrderDonService vipOrderDonService;

    private static final String SANDBOX_URL = "https://api-m.sandbox.paypal.com";
    private static final String LIVE_URL = "https://api-m.paypal.com";


    public String successPayment(String paymentId, String payerId,PaypalPayConfig paypalPayConfig) throws Exception {
        log.info("paypal回调 paymentId:{},payerId:{}",paymentId,payerId);
        Payment payment = null;
        try {
            payment = this.executePayment(paymentId,payerId,paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode());
        } catch (PayPalRESTException e) {
            log.error("支付回调失败",e);
            payment = Payment.get(getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()), paymentId);
        }

        PayerInfo payerInfo = payment.getPayer().getPayerInfo();
        log.info("paypal 支付回调 用户信息:{}", payerInfo);
        //支付成功
        if("approved".equals(payment.getState())){
            //paypal 交易id
            Sale sale = payment.getTransactions().get(0).getRelatedResources().get(0).getSale();
            log.info("paypal 支付回调 订单信息:{}", sale);
            Currency transactionFee = sale.getTransactionFee();
            String transationId = sale.getId();
            String orderId = payment.getTransactions().get(0).getCustom();
            //更新订单支付状态
            OrderDon orderDon = orderDonService.getById(orderId);
            orderDon.setTransactionId(transationId);
            orderDon.setPayTime(new Date());
            orderDon.setAbroadTransactionFee(new BigDecimal(transactionFee.getValue()).multiply(BigDecimal.valueOf(100)));
            orderDonService.orderNotify(orderDon);
            log.info("支付成功订单："+orderId+",支付交易号："+transationId);
            log.info("支付成功订单："+orderId+",返回参数："+payment);

            THREAD_POOL.execute(()->{
                try {
                    APIContext apiContext = this.getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
                    String baseUrl = "live".equals(paypalPayConfig.getMode()) ? LIVE_URL : SANDBOX_URL;
                    URL url = new URL(baseUrl + "/v1/shipping/trackers-batch");
                    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
                    httpConn.setRequestMethod("POST");
                    httpConn.setRequestProperty("Content-Type", "application/json");
                    httpConn.setRequestProperty("Authorization", apiContext.getAccessToken());
                    httpConn.setDoOutput(true);
                    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
                    writer.write("{\"trackers\": [{ \"transaction_id\": \"" + transationId + "\", \"status\": \"SHIPPED\"}]}");
                    writer.flush();
                    writer.close();
                    httpConn.getOutputStream().close();

                    InputStream responseStream = httpConn.getResponseCode() / 100 == 2 ? httpConn.getInputStream() : httpConn.getErrorStream();
                    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
                    String response = s.hasNext() ? s.next() : "";
                    log.info("添加物流信息 response:{}",response);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

            return redisService.getStr(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId);
        }
        log.info("支付失败返回参数："+ Jsons.toJson(payment));
        return paypalPayConfig.getCancelUrl();
    }


    public Payment createPayment(Long orderId,String successUrl) throws PayPalRESTException {
        OrderDon orderDon = orderDonService.getById(orderId);
        if (orderDon.getGoodsId() == 26) {
            GoodsDonSku sku = skuMapper.selectById(orderDon.getSkuId());
            if (!sku.getIsMirror()) {
                throw BusinessRuntimeException.getInstance("订单异常..");
            }
        }
        //paypal 不能购买奈飞月付账号 24-1-23
        if (orderDon.getSkuId() != null && orderDon.getSkuId() == 4) {
            throw BusinessRuntimeException.getInstance("暂不支持paypal购买该规格车票..");
        }
        redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout());
        BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN);
        orderDon.setAbroadMoney(money);
        orderDon.setType(OrderDon.Type.PAYPAL);
        orderDonService.updateById(orderDon);
        if (OrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"该订单已支付");
        }
        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
        // Set payer details
        Payer payer = new Payer();
        payer.setPaymentMethod("paypal");

        // Set redirect URLs
        RedirectUrls redirectUrls = new RedirectUrls();

        redirectUrls.setCancelUrl(paypalPayConfig.getCancelUrl());
        String returnUrl = "https://" + paypalPayConfig.getHost() + "/8081/api/applets/order/paypal/notify";
        //成功回调地址(自己写的/pay/success)
        redirectUrls.setReturnUrl(returnUrl);

        Transaction transaction = new Transaction();
        transaction.setDescription("");
        transaction.setCustom(orderDon.getId().toString());

        //订单价格
        Amount amount = new Amount();
        amount.setCurrency("USD");
        // 支付的总价，paypal会校验 total = subTotal + tax + ...
        BigDecimal totalMoney = money.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN);
        amount.setTotal(totalMoney.toString());
        // 设置各种费用
        Details details = new Details();
        //商品总价
        details.setSubtotal(totalMoney.toString());

        amount.setDetails(details);
        transaction.setAmount(amount);


        // Add transaction to a list
        List<Transaction> transactions = new ArrayList<Transaction>();
        transactions.add(transaction);

        // Add payment details
        Payment payment = new Payment();

        payment.setIntent("sale");
        payment.setPayer(payer);
        payment.setRedirectUrls(redirectUrls);
        payment.setTransactions(transactions);
        return payment.create(this.getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()));
    }

    private APIContext getAPIContext(String clientId,String clientSecret,String mode) throws PayPalRESTException {
        Map<String, String> sdkConfig = new HashMap<>();
        sdkConfig.put("mode", mode);
        OAuthTokenCredential authTokenCredential = new OAuthTokenCredential(clientId,clientSecret,sdkConfig);
        APIContext apiContext = new APIContext(authTokenCredential.getAccessToken());
        apiContext.setConfigurationMap(sdkConfig);
        return apiContext;
    }

    /**
     * 执行支付
     */
    public Payment executePayment(String paymentId, String PayerID,String clientId,String clientSecret,String mode) throws PayPalRESTException {
        Payment payment = new Payment();
        payment.setId(paymentId);
        PaymentExecution paymentExecute = new PaymentExecution();
        paymentExecute.setPayerId(PayerID);
        return payment.execute(this.getAPIContext(clientId,clientSecret,mode), paymentExecute);
    }


    public Boolean refund(OrderRefundReq refundReq, OrderDon orderDon) throws PayPalRESTException {
        BigDecimal net = refundReq.getRefundMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN).subtract(orderDon.getAbroadTransactionFee());;
        log.info("paypal return order:{},money:{}",orderDon.getId(),net);
        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
        APIContext apiContext = getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(),paypalPayConfig.getMode());
        Sale sale = Sale.get(apiContext, orderDon.getTransactionId());
        Refund refund = new Refund();
        refund.setAmount(new Amount("USD", net.divide(new BigDecimal(100),2,RoundingMode.HALF_DOWN).toString()));
        refund = sale.refund(apiContext, refund);
        log.info("paypal return refund:{}",refund);
        if ("completed".equals(refund.getState())){
            orderDon.setStatus(OrderDon.Status.refund);
            orderDonService.updateById(orderDon);
            return true;
        }
        return false;
    }

    public Payment createVipPayment(Long orderId, String successUrl) throws PayPalRESTException {
        VipOrderDon orderDon = vipOrderDonService.getById(orderId);
        redisService.set(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId,successUrl,RedisService.key.ABROAD_SUCCESS_URL.getTimeout());
        BigDecimal money = orderDon.getMoney().divide(new BigDecimal(7),2,RoundingMode.HALF_DOWN);
        orderDon.setAbroadMoney(money);
        orderDon.setType(VipOrderDon.Type.PAYPAL);
        vipOrderDonService.updateById(orderDon);
        if (VipOrderDon.Status.hasPayment.equals(orderDon.getStatus())) {
            throw new BusinessRuntimeException(GatewayApiCode.METADATA_SYSTEM_ERROR.getCode(),"该订单已支付");
        }
        PaypalPayConfig paypalPayConfig = paypalPayConfigService.getById(1);
        // Set payer details
        Payer payer = new Payer();
        payer.setPaymentMethod("paypal");

        // Set redirect URLs
        RedirectUrls redirectUrls = new RedirectUrls();

        redirectUrls.setCancelUrl(paypalPayConfig.getCancelUrl());
        String returnUrl = "https://" + paypalPayConfig.getHost() + "/8081/api/applet/vip/orders/paypal/notify";
        //成功回调地址(自己写的/pay/success)
        redirectUrls.setReturnUrl(returnUrl);

        Transaction transaction = new Transaction();
        transaction.setDescription("");
        transaction.setCustom(orderDon.getId().toString());

        //订单价格
        Amount amount = new Amount();
        amount.setCurrency("USD");
        // 支付的总价，paypal会校验 total = subTotal + tax + ...
        BigDecimal totalMoney = money.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN);
        amount.setTotal(totalMoney.toString());
        // 设置各种费用
        Details details = new Details();
        //商品总价
        details.setSubtotal(totalMoney.toString());

        amount.setDetails(details);
        transaction.setAmount(amount);


        // Add transaction to a list
        List<Transaction> transactions = new ArrayList<Transaction>();
        transactions.add(transaction);

        // Add payment details
        Payment payment = new Payment();

        payment.setIntent("sale");
        payment.setPayer(payer);
        payment.setRedirectUrls(redirectUrls);
        payment.setTransactions(transactions);
        return payment.create(this.getAPIContext(paypalPayConfig.getClientId(),paypalPayConfig.getClientSecret(),paypalPayConfig.getMode()));
    }

    public String successVipPayment(String paymentId, String payerId, PaypalPayConfig paypalPayConfig) throws Exception {
        log.info("paypal回调 paymentId:{},payerId:{}", paymentId, payerId);
        Payment payment = null;
        try {
            payment = this.executePayment(paymentId, payerId, paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
        } catch (PayPalRESTException e) {
            log.error("支付回调失败", e);
            payment = Payment.get(getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode()), paymentId);
        }

        PayerInfo payerInfo = payment.getPayer().getPayerInfo();
        log.info("paypal 支付回调 用户信息:{}", payerInfo);
        //支付成功
        if ("approved".equals(payment.getState())) {
            //paypal 交易id
            Sale sale = payment.getTransactions().get(0).getRelatedResources().get(0).getSale();
            log.info("paypal 支付回调 订单信息:{}", sale);
            Currency transactionFee = sale.getTransactionFee();
            String transationId = sale.getId();
            String orderId = payment.getTransactions().get(0).getCustom();
            //更新订单支付状态
            VipOrderDon orderDon = vipOrderDonService.getById(orderId);
            orderDon.setTransactionId(transationId);
            orderDon.setPayTime(new Date());
            orderDon.setAbroadTransactionFee(new BigDecimal(transactionFee.getValue()).multiply(BigDecimal.valueOf(100)));
            vipOrderDonService.orderNotify(orderDon);
            log.info("支付成功订单：" + orderId + ",支付交易号：" + transationId);
            log.info("支付成功订单：" + orderId + ",返回参数：" + payment);

            THREAD_POOL.execute(() -> {
                try {
                    APIContext apiContext = this.getAPIContext(paypalPayConfig.getClientId(), paypalPayConfig.getClientSecret(), paypalPayConfig.getMode());
                    String baseUrl = "live".equals(paypalPayConfig.getMode()) ? LIVE_URL : SANDBOX_URL;
                    URL url = new URL(baseUrl + "/v1/shipping/trackers-batch");
                    HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
                    httpConn.setRequestMethod("POST");
                    httpConn.setRequestProperty("Content-Type", "application/json");
                    httpConn.setRequestProperty("Authorization", apiContext.getAccessToken());
                    httpConn.setDoOutput(true);
                    OutputStreamWriter writer = new OutputStreamWriter(httpConn.getOutputStream());
                    writer.write("{\"trackers\": [{ \"transaction_id\": \"" + transationId + "\", \"status\": \"SHIPPED\"}]}");
                    writer.flush();
                    writer.close();
                    httpConn.getOutputStream().close();

                    InputStream responseStream = httpConn.getResponseCode() / 100 == 2 ? httpConn.getInputStream() : httpConn.getErrorStream();
                    Scanner s = new Scanner(responseStream).useDelimiter("\\A");
                    String response = s.hasNext() ? s.next() : "";
                    log.info("添加物流信息 response:{}", response);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            });

            return redisService.getStr(RedisService.key.ABROAD_SUCCESS_URL.getName() + orderId);
        }
        log.info("支付失败返回参数：" + Jsons.toJson(payment));
        return paypalPayConfig.getCancelUrl();
    }
}
