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

import com.alibaba.fastjson.JSON;
import com.crm.pay.dao.repository.CustomInfoRepository;
import com.crm.pay.dto.ucard.TxResult;
import com.crm.pay.entity.property.Va5PayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.Va5PayService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.pay.utils.DESedeUtil;
import com.crm.pay.utils.EncryptionHashQueryUtil;
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.CustomInfoTable;
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.*;
import com.google.common.base.Strings;
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.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2023-02-17 11:45
 */
@Slf4j
@Service
public class Va5PayServiceImpl extends BasePayServiceImpl implements Va5PayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private CustomInfoRepository customInfoRepository;

    private static String getSign(TreeMap<String, String> map, String orderId) throws Exception {
        map.remove("sign");
        StringBuilder signValue = new StringBuilder("");
        Set<String> keys = map.keySet();

        keys.forEach(key -> {
            signValue.append(map.get(key));
        });

        String sha1Result = ShaUtil.sha1(signValue.toString());
        String sign = MD5Util.getMD5(sha1Result);


        log.info(String.format("5pay--orderNo:%s,string:%s,sha1 string:%s,md5 sign string:%s", orderId,
                signValue.toString(), sha1Result,
                sign));

        return sign;
    }

    public static void main(String[] args) {
//        System.out.println(AESUtil.decrypt
//        ("RD7EYg93cTWYEjJSsiFAsxXdi6UYQf9jTemti00WEFjZkPaVJMhwSXASEavFJGIrsNv94idarZ
//        /Hczl7pwKnORjEjORaxSldR094lZMSq5OaPBPl7GqTvtMe7I33X0JLhMmpWD66nowCz
//        +ykos9MeA40XJ8TZtjsVsvt1WgVQ2f3pF7yZ5Hk3MnF61+xhOQZiGBdmC7HVhJC1zVTnsm+p2mGCzTz40ik4
//        /yPqzoNCO1dFJtepcXLIvKO8MbdRloI++qMaLQ9jyMjo2P60lPZ9K7CTxLmK6Syt5K89
//        /5ZZcZtJoGY8X0Z27VZBQvbZSzZCmu8ftrYTSiVh82DYVKMiqk65OwFSd+XsGPobSmekbSLBqfTJF6UIdhcMUGE94OYkfNbkVYP
//        /gcBDWZH8uOjF4v/Pwfdz743lXIa9LB9Mlwz4EXl6d9Xn4HwpwPIEf1L", "bfa5559109f94c78af615bcf00d52060"));
//        System.out.println(AESUtil.decrypt
//        ("RD7EYg93cTWYEjJSsiFAsxXdi6UYQf9jTemti00WEFjZkPaVJMhwSXASEavFJGIrIsQIvPSDync0orW2xocf45fqlRI616X3jBQrwg
//        /kAR2eOoxUdcsctKk86IAgjN7vBRFg6tcV1DFIVTb9G693KpI7vXNMksC7J22Ki4loI3bW0SdLnATiI7T3qxz9sD2ToMQ9g3l3EC
//        +wdaU2LE+cdtuQT1Qttn8lCenqvvc4NT4Ka7x+2thNKJWHzYNhUoyKXvE/HdbYMLQuniZbdo3GUmnpvhnO6Max+GI5zSO+0LRIZ3Fc
//        /Gp0ksbBQ6yMcL96P9aLkMHosXEdY1Nm2IW46n6K4XXgFZDJhU4FHmiSv0QYMwFA47BHYXLp
//        +T8KYJCTrgIWl1fAE84qqODMmTUEduKQBROdFXudNeJ61MYAJYk=\n", "bfa5559109f94c78af615bcf00d52060"));
//                System.out.println(AESUtil.decrypt("", "bfa5559109f94c78af615bcf00d52060"));
//                //va
//        System.out.println(AESUtil.encrypt("{\n" +
//                "    \"baseUrl\":\"https://cn-payment.my5pay.com/f2fOrder/CreateOrder\",\n" +
//                "    \"baseUrlEn\":\"https://en-payment.my5pay.com/f2fOrder/CreateOrder\",\n" +
//                "    \"merchantId\":\"53439\",\n" +
//                "    \"notifyUrl\":\"https://pay.cwgvu.com/5pay/va/callback\",\n" +
//                "    \"returnUrl\":\"https://pay.cwgvu.com/5pay/pickup\",\n" +
//                "    \"secretKey\":\"lOoqhiE5sga0wmkiewO8N9b4\"\n" +
//                "}", "bfa5559109f94c78af615bcf00d52060"));
//        //c2c
//        System.out.println(AESUtil.encrypt("{\n" +
//                "    \"baseUrl\":\"https://cn-payment.my5pay.com/c2cOrder/createorder\",\n" +
//                "    \"baseUrlEn\":\"https://en-payment.my5pay.com/c2cOrder/createorder\",\n" +
//                "    \"merchantId\":\"53439\",\n" +
//                "    \"notifyUrl\":\"https://pay.cwgvu.com/5pay/c2c/callback\",\n" +
//                "    \"returnUrl\":\"https://pay.cwgvu.com/5pay/pickup\",\n" +
//                "    \"secretKey\":\"lOoqhiE5sga0wmkiewO8N9b4\"\n" +
//                "}", "bfa5559109f94c78af615bcf00d52060"));
//        //f2f
//        System.out.println(AESUtil.encrypt("{\n" +
//                "    \"baseUrl\":\"https://cn-payment.my5pay.com/f2fOrder/checkout\",\n" +
//                "    \"baseUrlEn\":\"https://en-payment.my5pay.com/f2fOrder/checkout\",\n" +
//                "    \"merchantId\":\"53439\",\n" +
//                "    \"notifyUrl\":\"https://pay.cwgvu.com/5pay/f2f/callback\",\n" +
//                "    \"returnUrl\":\"https://pay.cwgvu.com/5pay/pickup\",\n" +
//                "    \"secretKey\":\"lOoqhiE5sga0wmkiewO8N9b4\"\n" +
//                "}", "bfa5559109f94c78af615bcf00d52060"));
    }

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());
        //通道配置实体
        Va5PayPropertyEntity payProperty = getPayProperty(channel, Va5PayPropertyEntity.class);

        FinanceDepositTable balanceTable = financeDepositService.save(balanceSaveEntity, channel);

        String formString = getFormString(payProperty, balanceTable, balanceSaveEntity.getPayType());
        balanceTable.setFormUrl(formString);
        
        return balanceTable.getSerial();
    }

    /**
     * 回调处理数据
     *
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(TreeMap<String, String> callbackEntity, String payType) throws Exception {

        log.info(String.format("5pay callback data:%s", JSON.toJSONString(callbackEntity)));
        //验证传输数据
        boolean flag = validate(callbackEntity, payType);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }

        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.get("merchantOrderNo"),
                JSON.toJSONString(callbackEntity)));
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity = null;
        BigDecimal amount = null;
        switch (payType) {
            case PayConstants.VA5_PAY_KEY_VA:
            case PayConstants.VA5_PAY_KEY_F2F:
                balanceCallbackUpdateEntity =
                        getFinanceDepositCallbackUpdateEntity(PayConstants.VA5PAY_SUCCESS_STATUS,
                                callbackEntity.get("merchantOrderNo"),
                                callbackEntity.get("orderNo"),
                                callbackEntity.get("status"), callbackEntity);
                amount = new BigDecimal(callbackEntity.get("orderAmount"));
                break;
            case PayConstants.VA5_PAY_KEY_C2C:
                balanceCallbackUpdateEntity =
                        getFinanceDepositCallbackUpdateEntity(PayConstants.VA5PAY_C2C_SUCCESS_STATUS,
                                callbackEntity.get("merchantOrderNo"),
                                callbackEntity.get("paymentAddress"),
                                callbackEntity.get("status"), callbackEntity);
                amount = new BigDecimal(callbackEntity.get("totalReceivedAmount"));
                break;
            default:
                break;
        }
        if (balanceCallbackUpdateEntity == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }

        balanceCallbackUpdateEntity.setTransformAmount(amount);
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);

    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getFormString(Va5PayPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                                 String payType) throws Exception {
        CustomInfoTable customInfoTable = customInfoRepository.getFirstById(balanceTable.getCustomId());
        TreeMap<String, String> postData = getPaymentRequestPostData(propertyEntity, balanceTable, customInfoTable,
                payType);
        String payUrl;
        if (CountryUtil.isCN(customInfoTable.getCountry())) {
            payUrl = propertyEntity.getBaseUrl();
        } else {
            payUrl = propertyEntity.getBaseUrlEn();
        }

        try {
            Map<String, String> headers = new HashMap<>(1);
            headers.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
            Connection.Response response = HttpUtil.post(headers, payUrl, postData);
        } catch (Exception e) {
            log.error(String.format("5pay callback error:%s", e.getMessage()));

        }

        String formString = PayFormUtil.getForm(payUrl, postData);

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        log.info(String.format("支付请求,请求orderNo:%s,请求formString:%s", orderNo, formString));
        return formString;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @return
     * @throws ServiceException
     */
    private boolean validate(TreeMap<String, String> callbackEntity, String payType) throws Exception {
        Va5PayPropertyEntity payProperty = getPayProperty(payType, false, Va5PayPropertyEntity.class);

        String _sign = callbackEntity.get("sign");
        String transaction = callbackEntity.get("transaction");
        if (!Strings.isNullOrEmpty(transaction)) {
            callbackEntity.remove("transaction");
        }
        String decryptMerchantOrderNo = DESedeUtil.decrypt(callbackEntity.get("merchantOrderNo"),
                payProperty.getSecretKey()).toLowerCase();
        String sign = getSign(callbackEntity, decryptMerchantOrderNo);

        String decryptOrderAmount =
                DESedeUtil.decrypt(callbackEntity.get("orderAmount"), payProperty.getSecretKey()).toLowerCase();
        String decryptStatus =
                DESedeUtil.decrypt(callbackEntity.get("status"), payProperty.getSecretKey()).toLowerCase();
        String decryptChannelName =
                DESedeUtil.decrypt(callbackEntity.get("channelName"), payProperty.getSecretKey()).toLowerCase();


        callbackEntity.put("sign", _sign);
        callbackEntity.put("orderAmount", decryptOrderAmount);
        callbackEntity.put("merchantOrderNo", decryptMerchantOrderNo);
        callbackEntity.put("status", decryptStatus);
        callbackEntity.put("channelName", decryptChannelName);
        if (!Strings.isNullOrEmpty(transaction)) {
            callbackEntity.put("transaction", transaction);
        }

        switch (payType) {
            case PayConstants.VA5_PAY_KEY_F2F:
            case PayConstants.VA5_PAY_KEY_VA:
                String decryptMemberId =
                        DESedeUtil.decrypt(callbackEntity.get("memberId"), payProperty.getSecretKey()).toLowerCase();
                String decryptCurrencyCode = DESedeUtil.decrypt(callbackEntity.get("currencyCode"),
                        payProperty.getSecretKey()).toLowerCase();
                String decryptOrderNo = DESedeUtil.decrypt(callbackEntity.get("orderNo"),
                        payProperty.getSecretKey()).toLowerCase();

                callbackEntity.put("memberId", decryptMemberId);
                callbackEntity.put("currencyCode", decryptCurrencyCode);
                callbackEntity.put("orderNo", decryptOrderNo);
                break;
            case PayConstants.VA5_PAY_KEY_C2C:
                String decryptPaymentAddress =
                        DESedeUtil.decrypt(callbackEntity.get("paymentAddress"), payProperty.getSecretKey()).toLowerCase();
                String decryptTotalReceivedAmount =
                        DESedeUtil.decrypt(callbackEntity.get("totalReceivedAmount"), payProperty.getSecretKey()).toLowerCase();

                callbackEntity.put("paymentAddress", decryptPaymentAddress);
                callbackEntity.put("totalReceivedAmount", decryptTotalReceivedAmount);

                FinanceDepositTable financeDepositTable = financeDepositService.getBySerial(callbackEntity.get(
                        "merchantOrderNo"));
                validHash(new BigDecimal(decryptOrderAmount), transaction, financeDepositTable);
                break;
            default:
                break;
        }


        log.info(String.format("order no:%s callback result sign compare--callback sign:%s, encryption sign result:%s"
                , decryptMerchantOrderNo, _sign, sign));
        return sign.equals(_sign);
    }

    private boolean validHash(BigDecimal decryptOrderAmount, String transaction,
                              FinanceDepositTable financeDepositTable) throws ServiceException {
        String unit = null;

        if (financeDepositTable.getBankCode().equals("2-USDT")) {
            unit = "tron";
        } else if (financeDepositTable.getTransformCurrency().equals("4-USDT")) {
            unit = "ethereum";
        } else {
            log.info("5pay通知回调：不验证hash，{}", financeDepositTable.getBankCode());
            return true;
        }

        TxResult txResult = null;

        try {
            EncryptionHashQueryUtil.query(JSON.parseArray(transaction).getJSONObject(0).getString(
                    "transactionHash"), unit);
        } catch (Exception e) {
            log.error("5pay通知回调：查询交易hash结果失败，{}", transaction);
            return false;
        }

        if (txResult.getAmount().compareTo(decryptOrderAmount) != 0) {
            log.error("5pay通知回调：通过hash：{} 比对金额不相同，{}", txResult.getAmount(),
                    decryptOrderAmount.stripTrailingZeros().toPlainString());
            return false;
        }
        return true;
    }

    public TreeMap<String, String> getPaymentRequestPostData(Va5PayPropertyEntity payProperty,
                                                             FinanceDepositTable balanceTable,
                                                             CustomInfoTable customInfoTable,
                                                             String payType) throws Exception {

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();

        TreeMap<String, String> postData = new TreeMap<String, String>();

        String encryptedMerchantOrderNo = DESedeUtil.encrypt(orderNo.toLowerCase(), payProperty.getSecretKey()).
                toLowerCase();
        postData.put("merchantId", payProperty.getMerchantId());
        postData.put("merchantOrderNo", encryptedMerchantOrderNo);
        postData.put("notifyUrl", payProperty.getNotifyUrl());
        postData.put("returnUrl", payProperty.getReturnUrl());

        switch (payType) {
            case PayConstants.VA5_PAY_KEY_VA:

                String encryptedMemberId = DESedeUtil.encrypt(balanceTable.getCId().toString(),
                        payProperty.getSecretKey()).toLowerCase();
                String encryptedCurrencyCode =
                        DESedeUtil.encrypt(balanceTable.getBankCode(), payProperty.getSecretKey()).toLowerCase();
                String encryptedOrderAmount = DESedeUtil.encrypt(String.format("%.2f",
                        balanceTable.getTransformAmount().doubleValue()), payProperty.getSecretKey()).toLowerCase();
                String encryptedEmail =
                        DESedeUtil.encrypt(customInfoTable.getEmail(), payProperty.getSecretKey()).toLowerCase();

                postData.put("memberId", encryptedMemberId);
                postData.put("currencyCode", encryptedCurrencyCode);
                postData.put("orderAmount", encryptedOrderAmount);
                postData.put("email", encryptedEmail);
                break;
            case PayConstants.VA5_PAY_KEY_C2C:

                String encryptedChainTypeId = DESedeUtil.encrypt(balanceTable.getBankCode().split("-")[0],
                        payProperty.getSecretKey()).toLowerCase();
                String encryptedChannelName = DESedeUtil.encrypt("C2C",
                        payProperty.getSecretKey()).toLowerCase();
                encryptedOrderAmount = DESedeUtil.encrypt(String.format("%.6f",
                        balanceTable.getTransformAmount().doubleValue()), payProperty.getSecretKey()).toLowerCase();

                postData.put("chainTypeId", encryptedChainTypeId);
                postData.put("channelName", encryptedChannelName);
                postData.put("orderAmount", encryptedOrderAmount);

                if (!"USDT".equals(balanceTable.getTransformCurrency())) {
                    String tokenId = null;
                    if ("ETH".equals(balanceTable.getTransformCurrency())) {
                        tokenId = "2";
                    } else if ("BNB".equals(balanceTable.getTransformCurrency())) {
                        tokenId = "4";
                    }
                    if (!Strings.isNullOrEmpty(tokenId)) {
                        postData.put("tokenId", DESedeUtil.encrypt(tokenId, payProperty.getSecretKey()).toLowerCase());
                    }
                }

                break;
            case PayConstants.VA5_PAY_KEY_F2F:
                encryptedMemberId =
                        DESedeUtil.encrypt(balanceTable.getCId().toString(), payProperty.getSecretKey()).toLowerCase();
                encryptedCurrencyCode =
                        DESedeUtil.encrypt(balanceTable.getBankCode(), payProperty.getSecretKey()).toLowerCase();
                encryptedOrderAmount = DESedeUtil.encrypt(String.format("%.2f",
                        balanceTable.getTransformAmount().doubleValue()), payProperty.getSecretKey()).toLowerCase();
                encryptedEmail =
                        DESedeUtil.encrypt(customInfoTable.getEmail(), payProperty.getSecretKey()).toLowerCase();

                postData.put("memberId", encryptedMemberId);
                postData.put("currencyCode", encryptedCurrencyCode);
                postData.put("orderAmount", encryptedOrderAmount);
                postData.put("email", encryptedEmail);
                break;
            default:
                break;
        }

        String sign = getSign(postData, orderNo);

        postData.put("sign", sign);

        return postData;
    }

}
