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

import com.alibaba.fastjson.JSON;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.acepayment.AcepaymentDataEntity;
import com.crm.pay.entity.acepayment.AcepaymentDepositEntity;
import com.crm.pay.entity.acepayment.AcepaymentOrderInfoDataEntity;
import com.crm.pay.entity.acepayment.AcepaymentOrderInfoEntity;
import com.crm.pay.entity.callback.AcepaymentCallbackEntity;
import com.crm.pay.entity.property.AcepaymentPropertyEntity;
import com.crm.pay.service.AcepaymentService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.SysChannelBankService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
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.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysChannelBankTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
import com.crm.rely.backend.util.AESUtil;
import com.crm.rely.backend.util.HAMCSHAUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MapUtil;
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-06-03 23:45
 */
@Slf4j
@Service
public class AcepaymentServiceImpl extends BasePayServiceImpl implements AcepaymentService {

    @Autowired
    private FinanceDepositService balanceService;

    @Autowired
    private SysChannelBankService sysChannelBankService;

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

        //获取通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.ACCEPAYMENT_PAY_TYPE_KEY);
        AcepaymentPropertyEntity payProperty = getPayProperty(channel, AcepaymentPropertyEntity.class);


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

        //获取支付地址
        String url = getUrl(balanceTable, payProperty);
        balanceTable.setFormUrl(url);
        return url;
    }

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


        boolean flag = validate(callbackEntity);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }
        AcepaymentPropertyEntity payProperty = getPayProperty(PayConstants.ACCEPAYMENT_PAY_TYPE_KEY, AcepaymentPropertyEntity.class);
        AcepaymentOrderInfoEntity acepaymentOrderInfoEntity = getOrderInfo(callbackEntity.getOrderSn(),
                callbackEntity.getStoreOrderSn(),
                payProperty);
        int status;
        switch (acepaymentOrderInfoEntity.getStatus()) {
            case "paid":
                status = 1;
                break;
            default:
                status = 0;
                break;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getStoreOrderSn(),
                        callbackEntity.getOrderSn() + "", status, callbackEntity);

        //计算实际金额
        BigDecimal amount = new BigDecimal(callbackEntity.getRealPrice());

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

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

        Map<String, String> params = new HashMap<>(6);
        params.put("app_id", payProperty.getAppId());
        params.put("store_order_sn", balanceTable.getSerial() + "_" + balanceTable.getLogin());
        params.put("good_name", balanceTable.getSerial());
        params.put("currency_chain_id", balanceTable.getBankCode());
        params.put("total_price", String.format("%.0f", balanceTable.getTransformAmount().doubleValue()));
        params.put("is_test", String.valueOf(payProperty.isTest()));

        String sign = getSign(params, payProperty);

        Map<String, Object> dataParams = new HashMap<>(7);
        dataParams.put("app_id", payProperty.getAppId());
        dataParams.put("store_order_sn", balanceTable.getSerial() + "_" + balanceTable.getLogin());
        dataParams.put("good_name", balanceTable.getSerial());
        dataParams.put("currency_chain_id", Integer.parseInt(balanceTable.getBankCode()));
        dataParams.put("total_price", Float.parseFloat(params.get("total_price")));
        dataParams.put("is_test", payProperty.isTest());
        dataParams.put("sign", sign);

        String data = JSON.toJSONString(dataParams);

        String payUrl = payProperty.getBaseUrl() + payProperty.getPayUrl();
        log.info(String.format("acepayment get create:%s,data:%s", payUrl, data));

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

        String payString = response.body();

        log.info("acepayment pay response body:" + payString);

        AcepaymentDataEntity acepaymentDataEntity = JSON.parseObject(payString, AcepaymentDataEntity.class);
        if (!acepaymentDataEntity.getCode().equals(0)) {
            throw ServiceException.exception("Payment platform is abnormal, please try again later or contact customer service.");
        }
        AcepaymentDepositEntity entityData = acepaymentDataEntity.getData();

        AcepaymentOrderInfoEntity acepaymentOrderInfoEntity = getOrderInfo(entityData.getOrderSn(),
                entityData.getStoreOrderSn(),
                payProperty);

        SysChannelBankTable channelBankTable = sysChannelBankService.getByCode(balanceTable.getBankCode(), PayConstants.ACCEPAYMENT_PAY_TYPE_KEY);
        return String.format(payProperty.getUrl(), acepaymentOrderInfoEntity.getAddress(),
                payProperty.getReturnUrl(),
                balanceTable.getTransformAmount().stripTrailingZeros().toPlainString(), channelBankTable.getEnName());

    }


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

        Map<String, String> params = new HashMap<>(7);
        params.put("app_id", callbackEntity.getAppId());
        params.put("currency", callbackEntity.getCurrency());
        params.put("currency_chain_id", callbackEntity.getCurrencyChainId());
        params.put("order_sn", callbackEntity.getOrderSn());
        params.put("total_price", callbackEntity.getTotalPrice());
        params.put("real_price", callbackEntity.getRealPrice());
        params.put("store_order_sn", callbackEntity.getStoreOrderSn());
        String sign = getSign(params);

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

    }

    public static void main(String[] args) {

        System.out.println(AESUtil.encrypt("", "bfa5559109f94c78af615bcf00d52060"));
    }

    private AcepaymentOrderInfoEntity getOrderInfo(String orderSn, String storeOrderSn,
                                                   AcepaymentPropertyEntity payProperty) throws IOException, ServiceException {
        Map<String, String> params = new HashMap<>(6);
        params.put("app_id", payProperty.getAppId());
        params.put("store_order_sn", storeOrderSn);
        params.put("order_sn", orderSn);

        String sign = getSign(params, payProperty);

        params.put("sign", sign);

        String payUrl = payProperty.getBaseUrl() + payProperty.getOrderInfoUrl() + "?" + MapUtil.getString(params);

        log.info(String.format("acepayment get order info:%s", payUrl));

        Connection.Response response = HttpUtil.get(payUrl);

        String payString = response.body();
        AcepaymentOrderInfoDataEntity acepaymentOrderInfoDataEntity = JSON.parseObject(payString, AcepaymentOrderInfoDataEntity.class);
        if (!acepaymentOrderInfoDataEntity.getCode().equals(0)) {
            throw ServiceException.exception("Payment platform is abnormal, please try again later or contact customer service.");
        }
        return acepaymentOrderInfoDataEntity.getData();
    }

    private String getSign(Map<String, String> params) throws Exception {
        AcepaymentPropertyEntity payProperty = getPayProperty(PayConstants.ACCEPAYMENT_PAY_TYPE_KEY, AcepaymentPropertyEntity.class);
        String signString = MapUtil.getStringSortByKey(params);
        String sign = HAMCSHAUtil.sha256HMAC(signString, payProperty.getSecret());
        log.info(String.format("callback--orderNo:%s,signString:%s,sign:%s", params.get("store_order_sn"), signString,
                sign));

        return sign;

    }

    private String getSign(Map<String, String> params, AcepaymentPropertyEntity payProperty) {
        String signString = MapUtil.getStringSortByKey(params);
        String sign = HAMCSHAUtil.sha256HMAC(signString, payProperty.getSecret());
        log.info(String.format("request--orderNo:%s,signString:%s,sign:%s", params.get("store_order_sn"), signString,
                sign));

        return sign;
    }
}
