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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dto.xpay.XPayOrderPriceDataDto;
import com.crm.pay.dto.xpay.XPayPaymentDataDto;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.property.XPayPropertyEntity;
import com.crm.pay.service.CustomInfoService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.XPayService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.pay.utils.BitcoinjECDSAUtil;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
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.util.AESUtil;
import com.crm.rely.backend.util.HAMCSHA256Util;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MapUtil;
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.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2024-04-23 11:45
 */
@Slf4j
@Service
public class XPayServiceImpl extends BasePayServiceImpl implements XPayService {
    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private CustomInfoService customInfoService;

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.XPAY_PAY_TYPE);
        //通道配置实体
        XPayPropertyEntity payProperty = getPayProperty(channel, XPayPropertyEntity.class);

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

        String url = payment(payProperty, balanceTable);
        balanceTable.setFormUrl(url);
        return url;
    }

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

        log.info(String.format("callback orderNo:%s,callback data:%s",
                callbackEntity.get("merchant_order_no"), JSON.toJSONString(callbackEntity)));

        if (!validate(callbackEntity)) {
            throw new ServiceException("fail");
        }

//通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.XPAY_PAY_TYPE);

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.XPAY_PAY_SUCCESS_STATUS,
                        callbackEntity.get("merchant_order_no"),
                        "",
                        callbackEntity.get("status"), callbackEntity);


//        if (channel.getTransformCurrency().equals(callbackEntity.get("legal_currency"))) {
//            balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.get("legal_amount")));
//        } else {
//
//            if (channel.getCurrency().equals(callbackEntity.get("currency"))) {
//                balanceCallbackUpdateEntity.setAmount(new BigDecimal(callbackEntity.get("count")));
//            } else if (channel.getTransformCurrency().equals(callbackEntity.get("currency"))) {
//                balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.get("count")));
//            }
//        }


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


    private XPayOrderPriceDataDto getOrderPrice(XPayPropertyEntity propertyEntity,
                                                FinanceDepositTable financeDepositTable) throws Exception {

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

        params.put("symbol", financeDepositTable.getCurrency());
        params.put("legal_currency", financeDepositTable.getTransformCurrency());


        String nonce = String.valueOf(System.currentTimeMillis());

        String sign = getSign(propertyEntity, params, propertyEntity.getOrderPriceUrl(), nonce);

        Map<String, String> headers = new HashMap<>(3);
        headers.put("BIZ-API-KEY", propertyEntity.getApiKey());
        headers.put("BIZ-API-SIGNATURE", sign);
        headers.put("BIZ-API-NONCE", nonce);

        String orderPriceUrl = propertyEntity.getBaseUrl() + propertyEntity.getOrderPriceUrl();

        log.info(String.format("get order price send rul:%s,params:%s,headers:%s", orderPriceUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));


        String data = JSON.toJSONString(params);

        Connection.Response response = HttpUtil.post(orderPriceUrl, headers, data);

        log.info(String.format("get order price response body is:%s", response.body()));

        JSONObject jsonObject = JSON.parseObject(response.body());

        if (!jsonObject.getInteger("code").equals(0)) {
            throw new PayValidatedException(jsonObject.getString("msg"));
        }

        XPayOrderPriceDataDto xPayOrderPriceDataDto = JSON.parseObject(jsonObject.getString("data"),
                XPayOrderPriceDataDto.class);


        return xPayOrderPriceDataDto;
    }

    private String payment(XPayPropertyEntity propertyEntity,
                           FinanceDepositTable financeDepositTable) throws Exception {

//        XPayOrderPriceDataDto xPayOrderPriceDataDto = getOrderPrice(propertyEntity, financeDepositTable);


        CustomInfoTable customInfoTable = customInfoService.getById(financeDepositTable.getCustomId());

        String nonce = String.valueOf(System.currentTimeMillis());
        Map<String, String> params = new HashMap<>(10);

        params.put("kyc_name", financeDepositTable.getName());
        params.put("kyc_id_card_type", "1");
//        params.put("kyc_card_num", customInfoTable.getIdentity());
        params.put("area_code", "+" + customInfoTable.getAreaCode());
        params.put("coin_name", financeDepositTable.getCurrency());
//        params.put("email", customInfoTable.getEmail());
        params.put("legal_currency", financeDepositTable.getTransformCurrency());
        params.put("coin_amount", String.format("%.0f",
                financeDepositTable.getAmount().doubleValue()));
//        params.put("legal_currency_amount", String.format("%.0f",
//                financeDepositTable.getAmount().multiply(xPayOrderPriceDataDto.getBuy()).doubleValue()));

        params.put("legal_currency_amount", String.format("%.0f",
                financeDepositTable.getTransformAmount().doubleValue()));

        params.put("return_url", propertyEntity.getReturnUrl());
        params.put("callback_url", propertyEntity.getCallbackUrl());
        params.put("merchant_order_no",
                financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin());

//        params.put("price_timestamp", xPayOrderPriceDataDto.getTimestamp());
        params.put("price_timestamp", nonce);

        String sign = getSign(propertyEntity, params, propertyEntity.getOrderCreateBuyUrl(), nonce);

        Map<String, String> headers = new HashMap<>(3);
        headers.put("BIZ-API-KEY", propertyEntity.getApiKey());
        headers.put("BIZ-API-SIGNATURE", sign);
        headers.put("BIZ-API-NONCE", nonce);

        String paymentUrl = propertyEntity.getBaseUrl() + propertyEntity.getOrderCreateBuyUrl();

        log.info(String.format("order:%s, payment send rul:%s,params:%s,headers:%s",
                params.get("merchant_order_no"), paymentUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));


        String data = JSON.toJSONString(params);

        Connection.Response response = HttpUtil.post(paymentUrl, headers, data);

//        Connection.Response response = HttpUtil.post(headers, paymentUrl, params);
//        Connection.Response response = HttpUtil.get(paymentUrl, headers);

        log.info(String.format("order:%s,payment response body is:%s", params.get("merchant_order_no"),
                response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());

        if (!jsonObject.getInteger("code").equals(0)) {
            throw new PayValidatedException(jsonObject.getString("msg"));
        }

        XPayPaymentDataDto xPayPaymentDataDto = JSON.parseObject(jsonObject.getString("data"),
                XPayPaymentDataDto.class);


        String url = xPayPaymentDataDto.getPayUrl();


        if (Strings.isNullOrEmpty(url)) {
            log.error("payment url is null");
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        log.info(String.format("xpay,payment orderNo:%s,url:%s",
                financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin(), url));

        financeDepositTable.setChannelSerial(xPayPaymentDataDto.getToken());
        return url;

    }

    private boolean validate(Map<String, String> callbackEntity) throws Exception {
        XPayPropertyEntity payProperty = getPayProperty(PayConstants.XPAY_PAY_TYPE, false,
                XPayPropertyEntity.class);

        String callbackSign = callbackEntity.get("sign");
        callbackEntity.remove("sign");
        String signString = composeParams(callbackEntity).replace("+", "%20");

        String sign = HAMCSHA256Util.HMACSHA256(signString,
                payProperty.getPubKey());

        log.info(String.format("order:%s,signString:%s,HMACSHA256:%s", callbackEntity.get("merchant_order_no"),
                signString,
                sign));


        return sign.equals(callbackSign);
    }

    private String getSign(XPayPropertyEntity propertyEntity, Map<String, String> params, String url, String nonce) throws Exception {
        String signString = composeParams(params);
        signString =
                "POST|" + url + "|" + nonce + "|" + signString;
        String sign = BitcoinjECDSAUtil.sign(propertyEntity.getPriECDSA(), signString);

        log.info(String.format("signString:%s,signECDSA:%s", signString, sign));

        return sign;
    }

    private static String composeParams(Map<String, String> params) {
        StringBuffer sb = new StringBuffer();
        MapUtil.sortByKey(params, false).forEach((s, o) -> {
            try {
                if ("kyc_name".equals(s)) {
                    sb.append(s).append("=").append(URLEncoder.encode(String.valueOf(o), "UTF-8").replace("+", "%20")).append("&");
                } else {
                    sb.append(s).append("=").append(URLEncoder.encode(String.valueOf(o), "UTF-8")).append("&");
                }

            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        });
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }


    public static void main(String[] args) throws UnsupportedEncodingException {
//        System.out.println(AESUtil.decrypt("", "bfa5559109f94c78af615bcf00d52060"));
//
        System.out.println(AESUtil.encrypt("", "bfa5559109f94c78af615bcf00d52060"));


        System.out.println(BitcoinjECDSAUtil.sign("bb06971bb6abb30da07f095a286e4e89ffd2b44cc83ea4d14fc68f85924884e1",
                "123123"));

        System.out.println(URLEncoder.encode(String.valueOf("Wei Li-T").replace(" ", "%20"), "UTF-8"));
        System.out.println(URLEncoder.encode(String.valueOf("Wei Li-T"), "UTF-8").replace("+", "%20"));

    }


}
