package com.paytend.api.service;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.paytend.api.utils.AESUtils;
import com.paytend.api.utils.PaytendUtil;
import com.paytend.api.utils.RSAUtils;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author wanghuaiqing
 * @description
 * @date 2023/9/12 15:18
 */
public class PayoutService {

    private static final Log log = LogFactory.get();

    private static final String PAYTEND_BASE_URL = "https://sandbox-api.paytend.com";
    private static final String PAYTEND_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCedW8W+zf1ni+uYu3lVBNGIPhBVIlKy4hnpjQ9VJQlWBpBofcD7K+lbH0UHnoJvreZGb4TajKuZVSJJeR5q2rUVEwlZrTQimWjN4zqMV5rBlBz06mxSMRz5B3ilsX1cccgoQOle/l9DCSqnLg6DpFmQmqMVrTuYv7Sr22PZf2DoQIDAQAB";
    private static final String PARTNER_ID = "888666000100260";
    private static final String PARTNER_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ51bxb7N/WeL65i7eVUE0Yg+EFUiUrLiGemND1UlCVYGkGh9wPsr6VsfRQeegm+t5kZvhNqMq5lVIkl5HmratRUTCVmtNCKZaM3jOoxXmsGUHPTqbFIxHPkHeKWxfVxxyChA6V7+X0MJKqcuDoOkWZCaoxWtO5i/tKvbY9l/YOhAgMBAAECgYEAmqXv8pThy3REAsUIeq2wVdwtjc0MZRP5H3dlOooGP+AZV/7Tjl/8GM2l0PHhlwssMl6eVGJr7ip085kov9odWm18ICuYni3d0SWtohMC1ZdlELNyOHX2zSVfvv2vRR5sGwFuubeW4Fn0wzn1cgFStCgv1O3TaYooUuAoAKkgzTECQQDJiXktB/hpmT7nRBMeOgq2ldepvHkCzvM7HdCe4XeLMa/QMvvW9kuv3n7l7hAJqWwvVFSKOwU9nY/ruI/bJ3RNAkEAyUfDY3YjytN2z5dK/adC9exwo6MaX9Nhn+IWIhXQjF4tY3EHHcSG4JH8/JXmgs3VwsgqeuCLNulBavEA5gzGpQJAJ1TS9zzU+BbWNeANMxr0uElp+1Ay9Z9ZzLG24yrxX5Lk0ucASyVqA/qSHWFmc9xCpsH4dbmq/ilfywsL2CNNCQJBAMcb5F65eUKk2P0VzqsJEKYxlGNrbyWn9wSwI/Dn33tblk6uermptTrlb3t11adDctfSJqOTCGPzbjEZ4yevL7kCQHzgXvHk1YIchcj2+TJe5bzagJFvKGHj0xlxbcbmG3r9dyvkAK9os3Tq3wEAfe7VCuKGcPrNdeMRG/eC/+oTfxM=";
    private static final String VERSION = "2.0";
    private static final String SIGN_TYPE = "RSA";

    public static void main(String[] args) throws Exception{
        /**
         * Create a payer   100161
         */
//        createPayer();

        /**
         * Update payer information
         */
//        updatePayer();

        /**
         * Query payer information
         */
//        queryPayer();
//        createPayee();
//        updatePayee();
//        queryPayee();
//        payment();
//        queryPaymentResult();
//        queryExchangeRate();
//        queryTransactionInformation();
    }

    /**
     * Create a payer
     *
     * @return
     * @throws Exception
     */
    public static boolean createPayer() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();

        Map<String, Object> address = new HashMap<>();
        address.put("addressLine",Base64.encode("Beijing"));
        address.put("addressLine2",Base64.encode("Chaoyang"));
        address.put("cityCode","CN_11");
        address.put("state","beijing");
        address.put("postcode","110110");
        address.put("countryCode","CN");

        // 设置值
        bizData.put("payerType", "2");
        bizData.put("companyName", "2");
        bizData.put("registrationNum", "2");
        bizData.put("registrationDate", "2023-01-01");
        bizData.put("firstName", "huaiqing");
        bizData.put("lastName", "wang");
        bizData.put("idno", "110101200007281509");
        bizData.put("idnoType", "1");
        bizData.put("idCountry", "CN");
        bizData.put("birthday", "2022-02-02");
        bizData.put("nationalityCode", "CN");
        bizData.put("mobile", "13211112222");
        bizData.put("occupation", "2323132");
        bizData.put("address", address);

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/createPayer", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * 修改付款人
     *
     * @return
     * @throws Exception
     */
    public static boolean updatePayer() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();

        Map<String, Object> address = new HashMap<>();
        address.put("addressLine",Base64.encode("ADDR1"));
        address.put("addressLine2",Base64.encode("ADDR2"));
        address.put("cityCode","CN_11");
        address.put("state","beijing");
        address.put("postcode","110110");
        address.put("countryCode","CN");

        // 设置值
        bizData.put("payerId","100180");
        bizData.put("payerType", "2");
        bizData.put("companyName", "2");
        bizData.put("registrationNum", "2222");
        bizData.put("registrationDate", "2023-02-02");
        bizData.put("firstName", "huaiqing");
        bizData.put("lastName", "wang");
        bizData.put("idno", "110101200007281509");
        bizData.put("idnoType", "1");
        bizData.put("idCountry", "CN");
        bizData.put("birthday", "2022-02-02");
        bizData.put("nationalityCode", "CN");
        bizData.put("mobile", "13211112222");
        bizData.put("occupation", "2323132");
        bizData.put("address", address);

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/updatePayer", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Query payer information
     *
     * @return
     * @throws Exception
     */
    public static boolean queryPayer() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
        bizData.put("payerId", "100180");
//        bizData.put("payerType", "2");
//        bizData.put("idno", "110101200007281508");


        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/getPayer", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Create a payee
     *
     * @return
     * @throws Exception
     */
    public static boolean createPayee() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();

        Map<String, Object> residenceAddress = new HashMap<>();
        residenceAddress.put("addressLine", Base64.encode("ADDR1"));
        residenceAddress.put("addressLine2", Base64.encode("ADDR2"));
        residenceAddress.put("cityCode", "2");
        residenceAddress.put("state", "2");
        residenceAddress.put("postcode", "2");
        residenceAddress.put("countryCode", "2");

        Map<String, Object> swiftPaymentBenInfo = new HashMap<>();
        swiftPaymentBenInfo.put("swiftCode", "2");
        swiftPaymentBenInfo.put("benAccountType", "15");
        swiftPaymentBenInfo.put("residenceAddress", residenceAddress);
        swiftPaymentBenInfo.put("bankCode", "2");

        Map<String, Object> address = new HashMap<>();
        address.put("addressLine", Base64.encode("2"));
        address.put("addressLine2", Base64.encode("2"));
        address.put("cityCode", "CN_11");
        address.put("state", "2");
        address.put("postcode", "2");
        address.put("countryCode", "CN");

        Map<String, Object> localPaymentBenInfo = new HashMap<>();
        localPaymentBenInfo.put("bankChannelId", "3541");
        localPaymentBenInfo.put("bankCode", "2");
        localPaymentBenInfo.put("bankAccountType", "2");
        localPaymentBenInfo.put("benAccountType", "2");
        localPaymentBenInfo.put("address", address);
        localPaymentBenInfo.put("benCompanyName", "2");
        localPaymentBenInfo.put("registrationNum", "2");
        localPaymentBenInfo.put("registrationDate", "2023-03-03");
        localPaymentBenInfo.put("taxId", "2");
        localPaymentBenInfo.put("benFirstName", "2");
        localPaymentBenInfo.put("benLastName", "2");
        localPaymentBenInfo.put("nationalityCode", "CN");
        localPaymentBenInfo.put("idnoType", "2");
        localPaymentBenInfo.put("idno", "2");
        localPaymentBenInfo.put("idExpirationDate", "2028-03-03");
        localPaymentBenInfo.put("birthday", "2011-03-03");
        localPaymentBenInfo.put("mobile", "2");

        // 设置值
        bizData.put("paymentType", "15");
        bizData.put("currency", "CNY");
        bizData.put("benAccountType", "2");
//        bizData.put("benAccountNum", "LT453120023810001149");
        bizData.put("benAccountNum", "+86");
        bizData.put("benAccountName", "Dong Qiang");
        bizData.put("swiftPaymentBenInfo", swiftPaymentBenInfo);
        bizData.put("localPaymentBenInfo", localPaymentBenInfo);


        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/createPayee", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Update payee
     *
     * @return
     * @throws Exception
     */
    public static boolean updatePayee() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();

        Map<String, Object> residenceAddress = new HashMap<>();
        residenceAddress.put("addressLine", Base64.encode("2"));
        residenceAddress.put("addressLine2", Base64.encode("2"));
        residenceAddress.put("cityCode", "2");
        residenceAddress.put("state", "2");
        residenceAddress.put("postcode", "2");
        residenceAddress.put("countryCode", "2");

        Map<String, Object> swiftPaymentBenInfo = new HashMap<>();
        swiftPaymentBenInfo.put("swiftCode", "2");
        swiftPaymentBenInfo.put("benAccountType", "2");
        swiftPaymentBenInfo.put("residenceAddress", residenceAddress);
        swiftPaymentBenInfo.put("bankCode", "2");

        Map<String, Object> address = new HashMap<>();
        address.put("addressLine", Base64.encode("222222"));
        address.put("addressLine2", Base64.encode("3333"));
        address.put("cityCode", "CN_11");
        address.put("state", "2");
        address.put("postcode", "24444");
        address.put("countryCode", "CN");

        Map<String, Object> localPaymentBenInfo = new HashMap<>();
        localPaymentBenInfo.put("bankChannelId", "3541");
        localPaymentBenInfo.put("bankCode", "2");
        localPaymentBenInfo.put("bankAccountType", "2");
        localPaymentBenInfo.put("benAccountType", "2");
        localPaymentBenInfo.put("address", address);
        localPaymentBenInfo.put("benCompanyName", "2");
        localPaymentBenInfo.put("registrationNum", "2");
        localPaymentBenInfo.put("registrationDate", "2023-03-03");
        localPaymentBenInfo.put("taxId", "2");
        localPaymentBenInfo.put("benFirstName", "2");
        localPaymentBenInfo.put("benLastName", "2");
        localPaymentBenInfo.put("nationalityCode", "CN");
        localPaymentBenInfo.put("idnoType", "2");
        localPaymentBenInfo.put("idno", "2");
        localPaymentBenInfo.put("idExpirationDate", "2028-03-03");
        localPaymentBenInfo.put("birthday", "2011-03-03");
        localPaymentBenInfo.put("mobile", "2");

        // 设置值
        bizData.put("payeeId", "100501");
        bizData.put("paymentType", "15");
        bizData.put("currency", "CNY");
        bizData.put("benAccountType", "2");
        bizData.put("benAccountNum", "+86");
        bizData.put("benAccountName", "Dong Qiang");
        bizData.put("swiftPaymentBenInfo", swiftPaymentBenInfo);
        bizData.put("localPaymentBenInfo", localPaymentBenInfo);


        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/updatePayee", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Query payee
     *
     * @return
     * @throws Exception
     */
    public static boolean queryPayee() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
//        bizData.put("payeeId", "100501");
        bizData.put("benAccountNum", "LT453120023810001149");
        bizData.put("currency", "EUR");
        bizData.put("paymentType", "2");
        bizData.put("bankChannelId", "");


        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/getPayee", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Payment
     *
     * @return
     * @throws Exception
     */
    public static boolean payment() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
        bizData.put("orderNo", IdUtil.simpleUUID());
        bizData.put("payAccountNum", "LT833120023810001144");
        bizData.put("payeeId", 100220L);
        bizData.put("benAccountNum", "LT453120023810001149");
        bizData.put("feeMethod", 1);
        bizData.put("localPaymentAmountType", 1);
        bizData.put("amount", 1000L);
        bizData.put("currency", "EUR");
        bizData.put("reference", "sssss");
        bizData.put("benRelationship", "sssss");
        bizData.put("sourceFunds", "sssss");
        bizData.put("payPurpose", "sssss");

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/payment", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Query payment result
     *
     * @return
     * @throws Exception
     */
    public static boolean queryPaymentResult() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
        bizData.put("orderNo", "6efe18e46bfb4cc2b181ba288e08543b");

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/getPaymentResult", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Query exchange rate
     * @return
     * @throws Exception
     */
    public static boolean queryExchangeRate() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
        bizData.put("payAccountNum", "LT833120023810001144");
        bizData.put("currency", "EUR");
        bizData.put("targetCurrency", "CNY");
        bizData.put("targetCountry", "CN");

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/getExchangeRate", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * Query transaction information
     * @return
     * @throws Exception
     */
    public static boolean queryTransactionInformation() throws Exception {
        // 创建一个新的HashMap
        Map<String, Object> bizData = new HashMap<>();
        // 设置值
        bizData.put("accountNum", "LT833120023810001144");
        bizData.put("currency", "EUR");
        bizData.put("beginDate", "2023-01-01");
        bizData.put("endDate", "2023-10-21");
//        bizData.put("tradeType", "121");
//        bizData.put("orderNo", "6efe18e46bfb4cc2b181ba288e08543b");
//        bizData.put("reference", "sss");
        bizData.put("start", "0");
        bizData.put("maxResult", "30");
        bizData.put("reconciliationDate", "2023-10-21");

        String requestStr = initCommonRequest(JSONUtil.toJsonStr(bizData),true);
        log.debug("REQUEST:{}", JSONUtil.toJsonPrettyStr(bizData));
        String responseStr = PaytendUtil.postJson(PAYTEND_BASE_URL + "/payout/queryTransactions", requestStr);
        handleResponse(responseStr);
        return true;
    }

    /**
     * 初始化 公共请求信息
     *
     * @return
     */
    public static String initCommonRequest(String requestStr, boolean isAES) throws Exception {
        String aesKey = AESUtils.generateKey();

        Map<String, Object> commonRequestMap = new HashMap<>();

        commonRequestMap.put("requestId", PaytendUtil.genOutTradeNo());
        commonRequestMap.put("partnerId", PARTNER_ID);
        commonRequestMap.put("version", VERSION);
        commonRequestMap.put("signType", SIGN_TYPE);
        commonRequestMap.put("bizData", requestStr);
        commonRequestMap.put("randomKey", aesKey);
        String signDataString = getTreeValue(commonRequestMap,false);
        System.out.println("signDataString:" + signDataString);
        commonRequestMap.put("signature", RSAUtils.signSHA256(signDataString.getBytes("UTF-8"),PARTNER_PRIVATE_KEY));

        commonRequestMap.put("bizData", isAES == true ? AESUtils.encrypt(requestStr, aesKey) : JSONUtil.parseObj(requestStr));
        commonRequestMap.put("randomKey", RSAUtils.encryptPublicKey(aesKey, PAYTEND_PUBLIC_KEY));
        return JSONUtil.toJsonStr(commonRequestMap);
    }
	
	    /**
     * 处理响应
     * @param responseStr
     * @return
     * @throws Exception
     */
    public static Map<String, Object> handleResponse(String responseStr) throws Exception {
        Map<String, Object> responseMap = JSONUtil.toBean(responseStr, Map.class);

        String respCode = String.valueOf(responseMap.get("respCode"));
        if(!"00".equals(respCode)){
            log.debug("Request exception");
            log.debug(JSONUtil.toJsonStr(responseStr));
            return responseMap;
        }

        String randomKey = String.valueOf(responseMap.get("randomKey"));
        String respData = String.valueOf(responseMap.get("respData"));
        String signature = String.valueOf(responseMap.get("signature"));

        /**
         * randomKey RSA 解密
         */
        String randomKeyMsg = RSAUtils.decryptPrivateKey(randomKey, PARTNER_PRIVATE_KEY);
        responseMap.put("randomKey",randomKeyMsg);
        /**
         * respData AES 解密
         */
        String respDataMsg = AESUtils.decrypt(respData, randomKeyMsg);
        responseMap.put("respData",JSONUtil.parseObj(respDataMsg));
        /**
         * 验证 signature
         */
        responseMap.remove("encryptFlag");// 预留字段 不参与签名
        responseMap.remove("signature"); // 不参与签名
        String signDataString = getTreeValue(responseMap, false); // 计算签名串
        boolean flag = RSAUtils.verifySHA256(signDataString.getBytes("UTF-8"), PAYTEND_PUBLIC_KEY, signature);

        if (flag) {
            responseMap.put("respData",JSONUtil.parse(respDataMsg));
            log.debug("RESPONSE:{}",JSONUtil.toJsonPrettyStr(responseMap));
        } else {
            log.debug("Response verification fail");
        }

        return responseMap;
    }


    public static String getTreeValue(Map<String, Object> toSortData, boolean emptyParamIsSign) {
        TreeMap<String, Object> params = new TreeMap<String, Object>();
        params.putAll(toSortData);
        StringBuffer orgin = new StringBuffer();
        Iterator<String> iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            String value = String.valueOf(params.get(key));
            if (emptyParamIsSign) {//空值参数参与签名
                orgin.append("&").append(key).append("=").append(value);
            } else {//空值参数不参与签名
                if (!StrUtil.isEmpty(value)) {
                    orgin.append("&").append(key).append("=").append(value);
                }
            }
        }
        return orgin.toString().substring(1);
    }

}
