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

import com.alibaba.fastjson.JSON;
import com.crm.pay.entity.callback.MtCallbackEntity;
import com.crm.pay.entity.property.MtPayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.MtService;
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.ServiceException;
import com.crm.rely.backend.util.MapUtil;
import com.crm.rely.backend.util.PayFormUtil;
import com.crm.rely.backend.util.SignUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: houn
 * @create: 2020-05-07 12:45
 */
@Slf4j
@Service
public class MtServiceImpl extends BasePayServiceImpl implements MtService {

    @Autowired
    private FinanceDepositService financeDepositService;

    /**
     * mt通道支付业务逻辑 包括加密 汇率等
     *
     * @param balanceSaveEntity 支付实体
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());

        MtPayPropertyEntity payProperty = getPayProperty(channel, MtPayPropertyEntity.class);

//        BigDecimal rate = getRate(payProperty);
//        balanceSaveEntity.setRate(rate);
        balanceSaveEntity.setRate(new BigDecimal(1));
        balanceSaveEntity.setCurrency(channel.getCurrency());
        balanceSaveEntity.setTransformCurrency(payProperty.getTarget());

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

        //获取form表单数据
        String formString = getForm(payProperty, financeDepositTable);

        //将form字符串加入到实体
        financeDepositTable.setFormUrl(formString);
        
        return financeDepositTable.getSerial();
    }

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

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

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity = getFinanceDepositCallbackUpdateEntity(PayConstants.MTPAY_SUCCESS_STATUS, callbackEntity.getOrderNo(), callbackEntity.getTradeOrderNo(), callbackEntity.getStatus(),callbackEntity);

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

    /**
     * 获取支付form
     *
     * @param financeDepositTable
     * @return
     * @throws ServiceException
     */
    private String getForm(MtPayPropertyEntity propertyEntity, FinanceDepositTable financeDepositTable) throws ServiceException {

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

        map.put("orderNo", financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin());
        map.put("price", (financeDepositTable.getTransformAmount().multiply(new BigDecimal(100)).intValue()) + "");
        if (PayConstants.PAY_MTPAY_IDR.equals(financeDepositTable.getPayType()) || PayConstants.PAY_MTPAY_VND.equals(financeDepositTable.getPayType())) {
            map.put("price", financeDepositTable.getTransformAmount().intValue() + "");
        } else {
            map.put("price", (financeDepositTable.getTransformAmount().multiply(new BigDecimal(100)).intValue()) + "");
        }
        map.put("merchantNo", propertyEntity.getShopNo());
        map.put("symbol", propertyEntity.getSymbol());

        map = MapUtil.sortByKey(map, false);
        String sign = getSign(propertyEntity, map);

        map.put("sign", sign);
        String formString = PayFormUtil.getForm(propertyEntity.getBaseUrl() + propertyEntity.getTradeUrl(), map);

        log.info(String.format("支付请求,请求orderNo:%s,请求form:%s", financeDepositTable.getId(), formString));

        return formString;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(MtCallbackEntity callbackEntity) throws Exception {
        MtPayPropertyEntity safePayProperty = getPayProperty(callbackEntity.getPayType(), false, MtPayPropertyEntity.class);

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

        map.put("orderNo", callbackEntity.getOrderNo());
        map.put("tradeOrderNo", callbackEntity.getTradeOrderNo());
        map.put("price", callbackEntity.getPrice());
        map.put("amount", callbackEntity.getAmount());
        map.put("exchangeRate", callbackEntity.getExchangeRate());
        map.put("merchantNo", callbackEntity.getMerchantNo());
        map.put("status", callbackEntity.getStatus());

        map = MapUtil.sortByKey(map, false);

        String signString = MapUtil.getString(map);
        boolean verify = SignUtil.verifySignSHA256withRSA(safePayProperty.getShopPublic(), signString, callbackEntity.getSign());

        return verify;
    }

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

        String signString = MapUtil.getString(map);

        String sign = SignUtil.signSHA256withRSA(propertyEntity.getCwgPrivate(), signString);

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

        return sign;
    }
}
