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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.callback.exlink.ExlinkDigitalPayCallbackEntity;
import com.crm.pay.entity.property.ExlinkDigitalPayPropertyEntity;
import com.crm.pay.service.ExlinkDigitalPayService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
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.PayValidatedException;
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.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.LinkedHashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2023-11-30 11:45
 */
@Slf4j
@Service
public class ExlinkDigitalPayServiceImpl extends BasePayServiceImpl implements ExlinkDigitalPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

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

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

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

        balanceSaveEntity.setCurrency(channel.getCurrency());
        balanceSaveEntity.setTransformCurrency(channel.getTransformCurrency());

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

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

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

        Map<String, Object> mapResult = new HashMap<>(4);
        ExlinkDigitalPayServiceImpl.log.info(String.format("callback orderNo:%s,callback data:%s",
                callbackEntity.getApiOrderNo(),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        ExlinkDigitalPayPropertyEntity payProperty = getPayProperty(PayConstants.EXLINK_DIGITAL_PAY_KEY, false,
                ExlinkDigitalPayPropertyEntity.class);

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            mapResult.put("code", 0);
            mapResult.put("message", "validate signature fail");
            mapResult.put("data", null);
            mapResult.put("success", false);
            return mapResult;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.EXLINK_DIGITAL_SUCCESS_STATUS,
                        callbackEntity.getApiOrderNo(),
                        callbackEntity.getTradeId(),
                        PayConstants.EXLINK_DIGITAL_SUCCESS_STATUS, callbackEntity);

        BigDecimal amount = new BigDecimal(callbackEntity.getAmount()).add(new BigDecimal(callbackEntity.getFee()));

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

        mapResult.put("code", 1);
        mapResult.put("message", "success");
        mapResult.put("data", null);
        mapResult.put("success", true);
        return mapResult;
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(ExlinkDigitalPayPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          FinanceDepositAddEntity balanceSaveEntity) throws ServiceException, IOException,
            PayValidatedException {

        balanceTable.setTransformAmount(balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP));
        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();

        Map<String, String> map = new LinkedHashMap<>(6);
        map.put("uid", propertyEntity.getUid());
        map.put("uniqueCode", String.valueOf(balanceSaveEntity.getCustomId()));
        map.put("protocol", balanceTable.getBankCode());
        map.put("coinName", balanceTable.getTransformCurrency());
        map.put("orderId", orderNo);
        map.put("amount", balanceTable.getTransformAmount().stripTrailingZeros().toPlainString());

        String sign = getSign(propertyEntity, map);

        map.put("signature", sign);
        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/json");

        Connection.Response response = HttpUtil.post(
                propertyEntity.getBaseUrl() + propertyEntity.getDigitalPayUrl(), headers, map);

        ExlinkDigitalPayServiceImpl.log.info(String.format("url:%s,json:%s,body:%s",
                propertyEntity.getBaseUrl() + propertyEntity.getDigitalPayUrl(),
                JSON.toJSON(map), response.body()));
        JSONObject requestEntity = JSON.parseObject(response.body());

        if (requestEntity == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        } else if ((requestEntity.get("code") != null && !requestEntity.get("code").toString().equals("1"))
                || (requestEntity.get("success") != null && !requestEntity.get("success").toString().equals("true"))) {
            throw new PayValidatedException(requestEntity.get("message").toString());
        }

        String url = String.valueOf(requestEntity.get("data"));
        ExlinkDigitalPayServiceImpl.log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("orderId"), url));
        return url;
    }

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

        String _sign = callbackEntity.getSignature();

        String sign = getSign(payProperty, callbackEntity);

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

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(ExlinkDigitalPayPropertyEntity propertyEntity, Map<String, String> map) {

        String signString = MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getAccessKey();
        String sign = MD5Util.getMD5(signString);
        ExlinkDigitalPayServiceImpl.log.info(String.format("exlink digital pay response--sign string:%s,sign:%s",
                signString,
                sign));


        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(ExlinkDigitalPayPropertyEntity propertyEntity,
                           ExlinkDigitalPayCallbackEntity callbackEntity) {
        Map<String, String> map = new LinkedHashMap<>(6);
        map.put("apiOrderNo", callbackEntity.getApiOrderNo());
        map.put("tradeId", callbackEntity.getTradeId());
        map.put("txId", callbackEntity.getTxId());
        map.put("uniqueCode", callbackEntity.getUniqueCode());
        map.put("protocol", callbackEntity.getProtocol());
        map.put("coinName", callbackEntity.getCoinName());
        map.put("amount", callbackEntity.getAmount());
        map.put("orderAmount", callbackEntity.getOrderAmount());
        map.put("fee", callbackEntity.getFee());
        String signString =
                MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getCallbackKey();
        String sign = MD5Util.getMD5(signString);

        ExlinkDigitalPayServiceImpl.log.info(String.format("回调结果加密处理--请求orderNo:%s,sign string:%s,sign:%s",
                callbackEntity.getApiOrderNo(),
                signString,
                sign));

        return sign;
    }
}
