package com.crm.pay.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.crm.pay.dto.kubo.KuboAuthDto;
import com.crm.pay.entity.callback.kubo.KuboCallbackEntity;
import com.crm.pay.entity.property.KuboPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.KuboService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.util.AESUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
import com.crm.rely.backend.util.PayFormUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: houn
 * @create: 2022-10-13 23:45
 */
@Slf4j
@Service
public class KuboServiceImpl extends BasePayServiceImpl implements KuboService {

    @Autowired
    private FinanceDepositService balanceService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {

        //获取通道配置
        SysRemittanceChannelTable channel = this.getPayChannel(PayConstants.KUBO_PAY_TYPE_KEY);
        KuboPropertyEntity payProperty = this.getPayProperty(channel, KuboPropertyEntity.class);
        balanceSaveEntity.setTransformCurrency(channel.getTransformCurrency());
        FinanceDepositTable balanceTable = this.balanceService.save(balanceSaveEntity, channel);

        //获取支付地址
        String from = this.getForm(balanceTable, payProperty);
        balanceTable.setFormUrl(from);
        
        return balanceTable.getSerial();
    }

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(KuboCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s",
                callbackEntity.getMerchantTransactionId(), JSON.toJSONString(callbackEntity)));


        boolean flag = this.validate(callbackEntity);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                this.getFinanceDepositCallbackUpdateEntity(PayConstants.KUBO_SUCCESS_STATUS,
                        callbackEntity.getMerchantTransactionId(),
                        callbackEntity.getPaymentId(),
                        callbackEntity.getStatus(),
                        callbackEntity);
        //计算实际金额
        BigDecimal amount = new BigDecimal(callbackEntity.getAmount());

        balanceCallbackUpdateEntity.setTransformAmount(amount);
        /**
         * 更新数据
         */
        this.balanceService.updateCallback(balanceCallbackUpdateEntity);
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getForm(FinanceDepositTable balanceTable, KuboPropertyEntity payProperty) throws ServiceException,
            IOException {

//        String auth = getAuth(payProperty);

        String sign = this.getSign(payProperty.getMemberId(),
                "KuboPay",
                String.format("%.2f", balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).doubleValue()),
                balanceTable.getSerial() + "_" + balanceTable.getLogin(),
                payProperty.getReturnUrl(),
                payProperty);

        Map<String, String> map = new HashMap<>(10);

        map.put("memberId", payProperty.getMemberId());
        map.put("totype", "KuboPay");
        map.put("amount", String.format("%.2f",
                balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).doubleValue()));
        map.put("currency", balanceTable.getCurrency());
        map.put("merchantTransactionId", balanceTable.getSerial() + "_" + balanceTable.getLogin());
        map.put("merchantRedirectUrl", payProperty.getReturnUrl());
        map.put("notificationUrl", payProperty.getNotifyUrl());

        map.put("checksum", sign);

        String payUrl = payProperty.getBaseUrl() + payProperty.getTransactionUrl();
        String formString = PayFormUtil.getForm(payUrl, map);

        log.info(String.format("支付请求,请求orderNo:%s,请求form:%s", balanceTable.getSerial(), formString));

        return formString;
    }

    private String getAuth(KuboPropertyEntity payProperty) throws IOException, ServiceException {
        String url = String.format(payProperty.getBaseUrl() + payProperty.getAuthTokeUrl() + "authentication" +
                        ".partnerId=%s"
                        + "&merchant.username=%s"
                        + "&authentication.sKey=%s", payProperty.getMemberId(), payProperty.getMemberName(),
                payProperty.getKey());
        Connection.Response response = HttpUtil.post(url);
        String authBodyString = response.body();
        log.info("kubo get auth response body:" + authBodyString);

        KuboAuthDto authDto = JSON.parseObject(authBodyString, KuboAuthDto.class);
        if (authDto != null) {
            return authDto.getAuthToken();
        }
        throw ServiceException.exception(Constants.SYSTEM_ERROR);

    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(KuboCallbackEntity callbackEntity) throws Exception {

        String sign = this.getSign(callbackEntity);

        log.info(String.format("order no:%s callback result sign compare--callback sign:%s,sign:%s",
                callbackEntity.getTmpl_amount(),
                callbackEntity.getChecksum(), sign));
        return callbackEntity.getChecksum().equals(sign);

    }

    public static void main(String[] args) {

        System.out.println(AESUtil.encrypt("{\n" +
                "    \"memberId\":\"15249\",\n" +
                "    \"memberName\":\"cwgmarketsbo\",\n" +
                "    \"key\":\"hmSPtKEa156Vr2tx2ioMARSuzawK1J9T\",\n" +
                "    \"notifyUrl\":\"https://testpay.cwgvu.com/kubp/callback\",\n" +
                "    \"returnUrl\":\"https://testpay.cwgvu.com/kubp/pickup\",\n" +
                "    \"baseUrl\":\"https://secure.kubopay.com\",\n" +
                "    \"authTokeUrl\":\"/transactionServices/REST/v1/authToken\",\n" +
                "    \"transactionUrl\":\"/transaction/Checkout\"\n" +
                "}", "bfa5559109f94c78af615bcf00d52060"));
    }


    private String getSign(KuboCallbackEntity callbackEntity) throws Exception {
        KuboPropertyEntity payProperty = this.getPayProperty(PayConstants.ACCEPAYMENT_PAY_TYPE_KEY, false,
                KuboPropertyEntity.class);
        String signString =
                callbackEntity.getPaymentId() + "|" + callbackEntity.getMerchantTransactionId() + "|" + callbackEntity.getAmount() + "|" + callbackEntity.getStatus() + "|" + payProperty.getKey();

        String sign = MD5Util.getMD5(signString);
        log.info(String.format("callback--orderNo:%s,signString:%s,sign:%s",
                callbackEntity.getMerchantTransactionId(), signString,
                sign));

        return sign;

    }

    private String getSign(String memberid, String totype, String amount, String merchantTransactionId,
                           String redirecturl, KuboPropertyEntity payProperty) {
        String signString =
                memberid + "|" + totype + "|" + amount + "|" + merchantTransactionId + "|" + redirecturl + "|" + payProperty.getKey();

        String sign = MD5Util.getMD5(signString);
        log.info(String.format("request--orderNo:%s,signString:%s,sign:%s", merchantTransactionId, signString,
                sign));

        return sign;
    }
}
