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

import com.alibaba.fastjson.JSON;
import com.crm.pay.dao.repository.FinanceDepositRepository;
import com.crm.pay.dto.g.GPayDto;
import com.crm.pay.entity.callback.GPayCallbackEntity;
import com.crm.pay.entity.g.pay.GPayDepositEntity;
import com.crm.pay.entity.property.GPayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.GPayService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
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.DateUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
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: 2022-06-20 11:45
 */
@Slf4j
@Service
public class GPayServiceImpl extends BasePayServiceImpl implements GPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private FinanceDepositRepository financeDepositRepository;

    @Override
    public boolean verificationLimitQuantity(Long customId,String payType) {

        return financeDepositRepository.existsByCustomIdAndPayTypeAndAddTimeGreaterThanEqual(customId,payType,
                DateUtil.parseDate());

    }

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());
        //通道配置实体
        GPayPropertyEntity payProperty = getPayProperty(channel, GPayPropertyEntity.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 GPayDto callback(GPayCallbackEntity callbackEntity, String payType) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getCompanyOrderNum(),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        GPayPropertyEntity payProperty = getPayProperty(payType, false, GPayPropertyEntity.class);

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {

            return new GPayDto(callbackEntity.getCompanyOrderNum(), callbackEntity.getMownecumOrderNum(), 0,
                    "validate error");
        }

        int status = 1;
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getCompanyOrderNum(), callbackEntity.getMownecumOrderNum(),
                        status, callbackEntity);

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

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

        return new GPayDto(callbackEntity.getCompanyOrderNum(), callbackEntity.getMownecumOrderNum(), 1,
                "success");
    }

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

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

        Map<String, String> map = new LinkedHashMap<>();
        map.put("company_id", propertyEntity.getCompanyId());
        map.put("amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        map.put("company_order_num", orderNo);
        map.put("company_user",String.valueOf(balanceTable.getLogin()));
        map.put("deposit_mode", String.valueOf(propertyEntity.getDepositMode()));
        map.put("group_id", propertyEntity.getGroupId());
        map.put("web_url", propertyEntity.getWebUrl());
        map.put("terminal", "2");
        map.put("client_ip", balanceSaveEntity.getIp());

        String sign = getRequestSign(propertyEntity, map);

        map.put("key", sign);

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

        log.info(String.format("url:%s,json:%s,body:%s", propertyEntity.getBaseUrl(),
                JSON.toJSON(map), response.body()));
        GPayDepositEntity gPayDepositEntity = JSON.parseObject(response.body(), GPayDepositEntity.class);

        if (gPayDepositEntity == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }

        if (gPayDepositEntity.getStatus().equals("0")) {
            throw new PayValidatedException(gPayDepositEntity.getErrorMsg());
        }

        String responseSign = getResponseSign(propertyEntity, gPayDepositEntity);
        if (!responseSign.equals(gPayDepositEntity.getKey())) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }

        String url = String.valueOf(gPayDepositEntity.getBreakUrl());
        log.info(String.format("gPay,orderNo:%s,url:%s", gPayDepositEntity.getCompanyOrderNum(), url));
        return url;
    }

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

        String _sign = callbackEntity.getKey();

        String sign = getCallbackSign(payProperty, callbackEntity);

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

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @return
     * @throws ServiceException
     */
    private String getRequestSign(GPayPropertyEntity propertyEntity, Map<String, String> map) {
        String signString =
                MD5Util.getMD5(propertyEntity.getKey())
                        + map.getOrDefault("company_id", "")
                        + map.getOrDefault("bank_id", "")
                        + map.getOrDefault("amount", "")
                        + map.getOrDefault("company_order_num", "")
                        + map.getOrDefault("company_user", "")
                        + map.getOrDefault("estimated_payment_bank", "")
                        + map.getOrDefault("deposit_mode", "")
                        + map.getOrDefault("group_id", "")
                        + map.getOrDefault("web_url", "")
                        + map.getOrDefault("memo", "")
                        + map.getOrDefault("note", "")
                        + map.getOrDefault("note_model", "");
        String sign = MD5Util.getMD5(signString);
        String order = map.get("company_order_num");
        if (Strings.isNullOrEmpty(order)) {
            log.info(String.format("g pay request--signString:%s,sign:%s", signString, sign));
        } else {
            log.info(String.format("g pay request--orderNo:%s,signString:%s,sign:%s", order, signString, sign));
        }

        return sign;
    }

    private String getResponseSign(GPayPropertyEntity propertyEntity, GPayDepositEntity depositEntity) {
        String signString =
                MD5Util.getMD5(propertyEntity.getKey())
                        + depositEntity.getBankCardNum()
                        + depositEntity.getBankAccName()
                        + depositEntity.getAmount()
                        + depositEntity.getEmail()
                        + depositEntity.getCompanyOrderNum()
                        + depositEntity.getDatetime()
                        + depositEntity.getNote()
                        + depositEntity.getMownecumOrderNum()
                        + depositEntity.getStatus()
                        + depositEntity.getErrorMsg()
                        + depositEntity.getMode()
                        + depositEntity.getIssuingBankAddress()
                        + depositEntity.getBreakUrl()
                        + depositEntity.getDepositMode()
                        + depositEntity.getCollectionBankId();

        String sign = MD5Util.getMD5(signString);

        log.info(String.format("g pay response--orderNo:%s,signString:%s,sign:%s", depositEntity.getCompanyOrderNum(), signString, sign));

        return sign;
    }


    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getCallbackSign(GPayPropertyEntity propertyEntity, GPayCallbackEntity callbackEntity) {
        String signString =
                MD5Util.getMD5(propertyEntity.getKey())
                        + callbackEntity.getPayTime()
                        + callbackEntity.getBankId()
                        + callbackEntity.getAmount()
                        + callbackEntity.getCompanyOrderNum()
                        + callbackEntity.getMownecumOrderNum()
                        + callbackEntity.getPayCardNum()
                        + callbackEntity.getPayCardName()
                        + callbackEntity.getChannel()
                        + callbackEntity.getArea()
                        + callbackEntity.getFee()
                        + callbackEntity.getTransactionCharge()
                        + callbackEntity.getDepositMode();
        String sign = MD5Util.getMD5(signString);

        log.info(String.format("g pay callback--orderNo:%s,signString:%s,sign:%s",
                callbackEntity.getCompanyOrderNum(), signString,
                sign));

        return sign;
    }

    public static void main(String[] args) {

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