package com.softwarebr.nunu.service.pay.ali.impl;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayRequest;
import com.alipay.api.AlipayResponse;
import com.alipay.api.domain.AlipayFundAuthOrderAppFreezeModel;
import com.alipay.api.domain.AlipayTradePayModel;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.brframework.commonweb.exception.HandleException;
import com.softwarebr.nunu.config.domain.ConfigService;
import com.softwarebr.nunu.domain.PayConfig;
import com.softwarebr.nunu.service.pay.ali.AliPayService;
import com.softwarebr.nunu.util.ali.OrderInfoUtil2_0;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Map;

/**
 * 支付宝支付
 *
 * @author xu
 * @date 2019/10/31 9:59
 */
@Service
@Slf4j
public class AliPayServiceImpl implements AliPayService {

    @Autowired(required = false)
    @Qualifier("appAlipayClient")
    AlipayClient appAlipayClient;

    @Autowired
    ConfigService<PayConfig> payConfigConfigService;


    @Override
    public String appAuthInfo() {
        PayConfig payConfig = payConfigConfigService.get();

        Map<String, String> authInfoMap = OrderInfoUtil2_0.buildAuthInfoMap(payConfig.getApPid(), payConfig.getAppApAppId(), System.currentTimeMillis() + "", true);
        String info = OrderInfoUtil2_0.buildOrderParam(authInfoMap);
        String sign = OrderInfoUtil2_0.getSign(authInfoMap, payConfig.getAppApPrivateKey(), true);
        return info + "&" + sign;
    }


    @Override
    public AlipaySystemOauthTokenResponse systemOauthToken(String code, String client) {
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setCode(code);
        request.setGrantType("authorization_code");
        return certificateExecute(request, client);


    }

    @Override
    public AlipayUserInfoShareResponse userInfoShare(String accessToken, String client) {
        AlipayUserInfoShareRequest request = new AlipayUserInfoShareRequest();
        return certificateExecute(request, accessToken, client);

    }

    @Override
    public AlipayFundAuthOrderUnfreezeResponse fundAuthOrderUnfreeze(String authNo, String outRequestNo, Long amount, String remark, String client) {
        PayConfig payConfig = payConfigConfigService.get();
        AlipayFundAuthOrderUnfreezeRequest request = new AlipayFundAuthOrderUnfreezeRequest();
        request.setBizContent("{" +
                "\"auth_no\":\"" + authNo + "\"," +
                "\"out_request_no\":\"" + outRequestNo + "\"," +
                "\"amount\":" + new BigDecimal(amount).divide(new BigDecimal(100)).toString() + "," +
                "\"remark\":\"" + remark + "\"," +
                "\"extra_param\":\"{\\\"unfreezeBizInfo\\\": \\\"{\\\\\\\"bizComplete\\\\\\\":\\\\\\\"true\\\\\\\"}\\\"}\"" +
                "  }");
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));
        return certificateExecute(request, client);
    }

    @Override
    public AlipayFundAuthOperationDetailQueryResponse fundAuthOperationDetailQuery(String authNo, String operationId, String client) {

        AlipayFundAuthOperationDetailQueryRequest request = new AlipayFundAuthOperationDetailQueryRequest();
        request.setBizContent("{" +
                "\"auth_no\":\"" + authNo + "\"," +
                "\"operation_id\":\"" + operationId + "\"" +
                "  }");
        return certificateExecute(request, client);
    }

    @Override
    public AlipayFundAuthOperationCancelResponse fundAuthOperationCancel(String authNo, String operationId, String remark, String client) {
        AlipayFundAuthOperationCancelRequest request = new AlipayFundAuthOperationCancelRequest();
        request.setBizContent("{" +
                "\"auth_no\":\"" + authNo + "\"," +
                "\"operation_id\":\"" + operationId + "\"," +
                "\"remark\":\"" + remark + "\"" +
                "  }");
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradePayResponse fundAuthTurnTradePay(String outTradeNo, String authNo,
                                                       Long totalAmount, String body, String subject,
                                                       String payerUserId,String client) {
        PayConfig payConfig = payConfigConfigService.get();
        AlipayTradePayRequest request = new AlipayTradePayRequest();

        AlipayTradePayModel model = new AlipayTradePayModel();
        // 预授权转支付商户订单号，为新的商户交易流水号；如果重试发起扣款，商户订单号不要变；
        model.setOutTradeNo(outTradeNo);
        // 固定值PRE_AUTH_ONLINE
        model.setProductCode("PRE_AUTH_ONLINE");
        // 填写预授权冻结交易号
        model.setAuthNo(authNo);
        // 解冻转支付标题，用于展示在支付宝账单中
        model.setSubject(subject);
        // 结算支付金额
        model.setTotalAmount(new BigDecimal(totalAmount).divide(new BigDecimal(100)).toString());
        // 填写卖家支付宝账户pid
        model.setSellerId(payConfig.getApPid());
        // 可填写备注信息
        model.setBody(body);
        // 购买者
        model.setBuyerId(payerUserId);
        //必须使用COMPLETE,传入该值用户剩余金额会自动解冻
        model.setAuthConfirmMode("COMPLETE");
        request.setBizModel(model);
        //异步通知地址，必填，该接口只通过该参数进行异步通知
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradeQueryResponse tradeQuery(String outTradeNo, String client) {
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\""+ outTradeNo + "\"" +
                "  }");
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradeCloseResponse tradeClose(String outTradeNo, String client) {
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + outTradeNo + "\"" +
                "  }");
        PayConfig payConfig = payConfigConfigService.get();
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradeRefundResponse tradeRefund(String outTradeNo, Long refundAmount, String client) {
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + outTradeNo + "\"," +
                "\"refund_amount\":" + new BigDecimal(refundAmount).divide(new BigDecimal(100)) +
                "  }");
        PayConfig payConfig = payConfigConfigService.get();
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradeFastpayRefundQueryResponse tradeFastpayRefundQuery(String outTradeNo, String client) {
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + outTradeNo + "\"," +
                "\"out_request_no\":\"" + outTradeNo + "\"" +
                "  }");
        return certificateExecute(request, client);
    }

    @Override
    public AlipayTradeAppPayResponse tradeAppPay(String outTradeNo, Long totalAmount,
                                                 String body, String subject,
                                                 String timeoutExpress, String client) {
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        request.setBizContent("{" +
                "\"timeout_express\":\"" + timeoutExpress + "\"," +
                "\"total_amount\":\"" + new BigDecimal(totalAmount).divide(new BigDecimal(100)) + "\"," +
                "\"product_code\":\"QUICK_MSECURITY_PAY\"," +
                "\"body\":\"" + body + "\"," +
                "\"subject\":\"" + subject + "\"," +
                "\"out_trade_no\":\"" + outTradeNo + "\"" +
                "  }");
        PayConfig payConfig = payConfigConfigService.get();
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));
        return sdkExecute(request, client);
    }

    @Override
    public AlipayFundAuthOrderAppFreezeResponse
    fundAuthOrderAppFreeze(String orderTitle, String outOrderNo, String category, String outStoreAlias,
                           Long amount, String payTimeout, String client) {
        PayConfig payConfig = payConfigConfigService.get();
        AlipayFundAuthOrderAppFreezeRequest request = new AlipayFundAuthOrderAppFreezeRequest();
        AlipayFundAuthOrderAppFreezeModel model = new AlipayFundAuthOrderAppFreezeModel();
        model.setOrderTitle(orderTitle);
        model.setOutOrderNo(outOrderNo);
        model.setOutRequestNo(outOrderNo);
        model.setPayeeUserId(payConfig.getApPid());
        //PRE_AUTH_ONLINE为固定值，不要替换
        model.setProductCode("PRE_AUTH_ONLINE");
        model.setAmount(new BigDecimal(amount).divide(new BigDecimal(100)).toString());
        model.setPayTimeout(payTimeout);
        //需要支持信用授权，该字段必传，outStoreAlias将在用户端信用守护、支付信息、账单详情页展示
        model.setExtraParam("{\"category\":\"" + category +
                "\",\"outStoreCode\":\"charge001\",\"outStoreAlias\":\"" + outStoreAlias +
                "\"}");
        request.setBizModel(model);
        //异步通知地址，必填，该接口只通过该参数进行异步通知
        request.setNotifyUrl(getPayNotifyUrl(client, payConfig));

        return sdkExecute(request, client);

    }

    private AlipayClient getClient(String client) {
        switch (client) {
            case CLIENT_APP:
                return appAlipayClient;
            default:
                throw new HandleException("找不到" + client + "相关的支付宝客户端，请检查是否已经初始化");
        }
    }

    private String getPayNotifyUrl(String client, PayConfig payConfig) {
        switch (client) {
            case CLIENT_APP:
                return payConfig.getAppApPayNotifyUrl();
            default:
                throw new HandleException("找不到" + client + "相关的支付宝客户端，请检查是否已经初始化");
        }
    }

    private void checkResponse(AlipayResponse response) {
        if (!response.isSuccess()) {
            log.error("支付宝调用失败，错误代码：{}, 错误信息：{}", response.getSubCode(), response.getSubMsg());
            throw new HandleException("支付宝调用失败");
        }
    }

    private <T> T certificateExecute(AlipayRequest request, String client){
        try {
            AlipayResponse response = getClient(client).certificateExecute(request);
            checkResponse(response);
            return (T) response;
        } catch (AlipayApiException e) {
            log.error("支付宝调用失败", e);
            throw new HandleException("支付宝调用失败");
        }
    }

    private <T> T certificateExecute(AlipayRequest request, String accessToken, String client){
        try {
            AlipayResponse response = getClient(client).certificateExecute(request, accessToken);
            checkResponse(response);
            return (T) response;
        } catch (AlipayApiException e) {
            log.error("支付宝调用失败", e);
            throw new HandleException("支付宝调用失败");
        }
    }

    private <T> T sdkExecute(AlipayRequest request, String client){
        try {
            AlipayResponse response = getClient(client).sdkExecute(request);
            checkResponse(response);
            return (T) response;
        } catch (AlipayApiException e) {
            log.error("支付宝调用失败", e);
            throw new HandleException("支付宝调用失败");
        }
    }
}
