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

import com.alibaba.fastjson.JSON;
import com.crm.pay.entity.flash.FlashExOrderEntity;
import com.crm.pay.entity.flash.FlashExRateEntity;
import com.crm.pay.entity.flash.FlashExRateRequestEntity;
import com.crm.pay.entity.flash.FlashExRequestEntity;
import com.crm.pay.entity.property.FlashExPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.FlashExService;
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.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.Map;

import static com.alibaba.fastjson.JSON.parseObject;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2020-09-14 11:45
 */
@Slf4j
@Service
public class FlashExServiceImpl extends BasePayServiceImpl implements FlashExService {
    @Autowired
    private FinanceDepositService financeDepositService;

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

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

        if (payProperty.getApiAmountType() != null && payProperty.getApiAmountType().equals("2")) {
            BigDecimal rate = getRate(payProperty, payProperty.getApiAmountType());
            balanceSaveEntity.setRate(rate);
        }
        balanceSaveEntity.setCurrency(channel.getCurrency());
        balanceSaveEntity.setTransformCurrency(channel.getTransformCurrency());

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

        String url = getUrl(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("apiOrderId"), JSON.toJSONString(callbackEntity)));

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

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.FLASHEX_SUCCESS, callbackEntity.get("apiOrderId"), callbackEntity.get("tradeId"), Integer.parseInt(callbackEntity.get("tradeStatus")), callbackEntity);

        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.get("amount")));
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
    }

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

        FlashExOrderEntity flashExOrderEntity = new FlashExOrderEntity();
        flashExOrderEntity.setAppId(propertyEntity.getShopNo());
        flashExOrderEntity.setApiUserId(balanceTable.getCustomId().toString());

        flashExOrderEntity.setApiAmountType(propertyEntity.getApiAmountType());
        //判断通道货币选择类型
        if (propertyEntity.getApiAmountType() != null && propertyEntity.getApiAmountType().equals("2")) {
            flashExOrderEntity.setAmount(balanceTable.getAmount().setScale(0, BigDecimal.ROUND_UP).toString());
        } else {

            flashExOrderEntity.setAmount(balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).toString());
        }
        flashExOrderEntity.setApiOrderType(propertyEntity.getApiOrderType());
        flashExOrderEntity.setTradeType(propertyEntity.getTradeType());
        flashExOrderEntity.setApiOrderId(balanceTable.getSerial() + "_" + balanceTable.getLogin());
        flashExOrderEntity.setTimeStamp(String.valueOf(balanceTable.getAddTime().getTime() / 1000));
        flashExOrderEntity.setNotifyUrl(propertyEntity.getNotifyUrl());

        String sign = getSign(propertyEntity, flashExOrderEntity);

        flashExOrderEntity.setSign(sign);

        Map<String, String> headers = new HashMap<>(2);
        headers.put("Content-Type", "application/json");
        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getOrderUrl(), headers, flashExOrderEntity);

        FlashExRequestEntity flashExRequestEntity = parseObject(response.body(), FlashExRequestEntity.class);
        if (flashExRequestEntity == null || flashExRequestEntity.getCode() == null || !flashExRequestEntity.getCode().equals(200) || flashExRequestEntity.getResult() == null) {

            if (flashExRequestEntity != null && flashExRequestEntity.getCode() != null && !flashExRequestEntity.getCode().equals(200)) {
                throw new PayValidatedException(flashExRequestEntity.getMsg());
            }
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }
        String url = flashExRequestEntity.getResult().getUrl();
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", flashExOrderEntity.getApiOrderId(), url));
        return url;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(Map<String, String> callbackEntity) throws Exception {
        FlashExPropertyEntity payProperty = getPayProperty(PayConstants.FLASHEX_TYPE_KEY, false, FlashExPropertyEntity.class);
        String _sign = callbackEntity.get("_sign");
        String sign = getSign(payProperty, callbackEntity);

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

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @param flashExOrderEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(FlashExPropertyEntity propertyEntity, FlashExOrderEntity flashExOrderEntity) {

        Map<String, String> map = new HashMap<>(9);
        map.put("appId", flashExOrderEntity.getAppId());
        map.put("apiUserId", flashExOrderEntity.getApiUserId());
        map.put("amount", flashExOrderEntity.getAmount());
        map.put("apiAmountType", flashExOrderEntity.getApiAmountType());
        map.put("apiOrderType", flashExOrderEntity.getApiOrderType());
        map.put("tradeType", flashExOrderEntity.getTradeType());
        map.put("apiOrderId", flashExOrderEntity.getApiOrderId());
        map.put("timeStamp", flashExOrderEntity.getTimeStamp());
        map.put("notifyUrl", flashExOrderEntity.getNotifyUrl());

        String signString = MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getShopKey();

        String sign = MD5Util.getMD5(signString);

        log.info(String.format("orderNo:%s,sign string:%s,sign:%s", flashExOrderEntity.getApiOrderId(),
                signString, sign));

        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(FlashExPropertyEntity propertyEntity, Map<String, String> callbackEntity) {

        callbackEntity.remove("_sign");
        String signString = MapUtil.getString(MapUtil.sortByKey(callbackEntity, false)) + "&key=" + propertyEntity.getCallbackKey();
        String sign = MD5Util.getMD5(signString);

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

        return sign;
    }

    private String getSign(FlashExPropertyEntity propertyEntity, FlashExRateEntity flashExRateEntity) {

        Map<String, String> map = new HashMap<>(9);
        map.put("appId", flashExRateEntity.getAppId());
        map.put("coinCode", flashExRateEntity.getCoinCode());
        map.put("timeStamp", flashExRateEntity.getTimeStamp());

        String signString = MapUtil.getString(MapUtil.sortByKey(map, false)) + "&key=" + propertyEntity.getShopKey();

        String sign = MD5Util.getMD5(signString);

        log.info(String.format("支付请求汇率处理--请求货币类型:%s,sign string:%s,sign:%s", flashExRateEntity.getCoinCode(), signString, sign));

        return sign;
    }

    private BigDecimal getRate(FlashExPropertyEntity payProperty, String apiAmountType) throws Exception {
        FlashExRateEntity flashExRateEntity = new FlashExRateEntity();
        flashExRateEntity.setAppId(payProperty.getShopNo());
        if (apiAmountType != null && apiAmountType.equals("2")) {
            flashExRateEntity.setCoinCode("USDT");
        } else {
            flashExRateEntity.setCoinCode("CNY");
        }
        flashExRateEntity.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));

        String sign = getSign(payProperty, flashExRateEntity);
        flashExRateEntity.setSign(sign);

        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/json");
        Connection.Response response = HttpUtil.post(payProperty.getBaseUrl() + payProperty.getRateUrl(), headers, flashExRateEntity);

        FlashExRateRequestEntity flashExRateRequestEntity = JSON.parseObject(response.body(), FlashExRateRequestEntity.class);

        if (flashExRateRequestEntity == null
                || flashExRateRequestEntity.getCode() == null
                || !flashExRateRequestEntity.getCode().equals(200)
                || flashExRateRequestEntity.getResult() == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }
        BigDecimal rate = new BigDecimal(flashExRateRequestEntity.getResult().getPrice());

        return rate;
    }
}
