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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.callback.AcePayCallbackEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.property.AcePayPropertyEntity;
import com.crm.pay.service.AcePayService;
import com.crm.pay.service.FinanceDepositService;
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.core.exception.PayValidatedException;
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.SysRemittanceChannelTable;
import com.crm.rely.backend.util.GetRandom;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
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.IOException;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2021-05-31 11:45
 */
@Slf4j
@Service
public class AcePayServiceImpl extends BasePayServiceImpl implements AcePayService {

    @Autowired
    private FinanceDepositService financeDepositService;

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.ACE_PAY_KEY);
        //通道配置实体
        AcePayPropertyEntity payProperty = getPayProperty(channel, AcePayPropertyEntity.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 void callback(AcePayCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getOut_trade_no(), JSON.toJSONString(callbackEntity)));
        //获取配置
        AcePayPropertyEntity payProperty = getPayProperty(PayConstants.ACE_PAY_KEY, false,AcePayPropertyEntity.class);

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }

        int status;
        switch (callbackEntity.getTrade_state()) {
            case PayConstants.ACE_SUCCESS_STATUS:
                status = PayConstants.CALLBACE_SUCCESS_STATUS;
                break;
            case PayConstants.ACE_EXPIRE_STATUS:
                status = PayConstants.CALLBACE_FAIL_STATUS;
                break;
            case PayConstants.ACE_WAIT_STATUS:
                status = 0;
                break;
            default:
                status = -1;
                break;
        }
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getOut_trade_no(), callbackEntity.getMech_trade_no(),
                        status,callbackEntity);

        BigDecimal amount = new BigDecimal(callbackEntity.getTotal_fee());

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

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

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

        String[] currencys = balanceTable.getBankCode().split("-");
        if (currencys.length < 2) {
            throw ServiceException.exception(Constants.FAIL);
        }

        String nonceStr = GetRandom.getRandom(8);
        Map<String, String> map = new LinkedHashMap<>();
        map.put("appid", propertyEntity.getAppId());
        map.put("mch_id", propertyEntity.getMchId());
        map.put("nonce_str", nonceStr);
        map.put("goods", "");
        map.put("out_trade_no", orderNo);
        map.put("main_coin", currencys[0]);
        map.put("sub_coin", currencys[1]);
        map.put("total_fee", String.format("%.2f", balanceTable.getAmount().doubleValue()));
        map.put("spbill_create_ip", balanceSaveEntity.getIp());
//        map.put("spbill_create_ip", "43.226.126.238");
//        map.put("spbill_create_ip", "112.213.107.185");
        map.put("notify_url", propertyEntity.getNotifyUrl());
        map.put("jump_url", propertyEntity.getJumpUrl());

        String sign = getSign(propertyEntity, map);

        map.put("sign", sign);

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

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

        if (requestEntity == null || (requestEntity.get("return_code") != null && !requestEntity.get("return_code").equals("0"))) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }

        map.clear();
        String resultSign = null;
        for (Map.Entry<String, Object> mapjson : requestEntity.entrySet()) {
            if ("sign".equals(mapjson.getKey())) {
                resultSign = String.valueOf(mapjson.getValue());
            } else {
                if (!"0".equals(String.valueOf(mapjson.getValue()))) {
                    map.put(mapjson.getKey(), String.valueOf(mapjson.getValue()));
                }
            }
        }


        sign = getSign(propertyEntity, map);
        if (!sign.equals(resultSign)) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }

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

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

        String _sign = callbackEntity.getSign();

        String sign = getSign(payProperty, callbackEntity);

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

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

        String signString = MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getKey();
        String sign = MD5Util.getMD5(signString).toUpperCase();
        String order = map.get("out_trade_no");
        if (Strings.isNullOrEmpty(order)) {
            log.info(String.format("ace pay response--sign string:%s,sign:%s", signString, sign));
        } else {
            log.info(String.format("orderNo:%s,sign string:%s,sign:%s", order, signString, sign));
        }


        return sign;
    }


    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(AcePayPropertyEntity propertyEntity, AcePayCallbackEntity callbackEntity) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("mech_trade_no", callbackEntity.getMech_trade_no());
        map.put("out_trade_no", callbackEntity.getOut_trade_no());
        map.put("main_coin", callbackEntity.getMain_coin());
        map.put("sub_coin", callbackEntity.getSub_coin());
        map.put("trade_state", callbackEntity.getTrade_state());
        map.put("total_fee", callbackEntity.getTotal_fee());
        map.put("complete_time", callbackEntity.getComplete_time());
        map.put("nonce_str", callbackEntity.getNonce_str());
        String signString = MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getKey();
        String sign = MD5Util.getMD5(signString).toUpperCase();

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

        return sign;
    }


}
