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

import com.alibaba.fastjson.JSON;
import com.crm.pay.dto.anx.AnxResultDto;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.anx.AnxDepositEntity;
import com.crm.pay.entity.callback.AnxCallbackEntity;
import com.crm.pay.entity.property.AnxPropertyEntity;
import com.crm.pay.service.AnxService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
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.*;
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.HashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2022-06-22 11:45
 */
@Slf4j
@Service
public class AnxServiceImpl extends BasePayServiceImpl implements AnxService {
    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) {

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

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

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

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

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

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

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

        boolean flag = validate(callbackEntity);
        if (!flag) {
            return new AnxResultDto("ERROR", "validate error");
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.ANX_PAY_SUCCESS, callbackEntity.getSeqId(),
                        "", callbackEntity.getStat(),
                        callbackEntity);

        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getAmount()));
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
        return new AnxResultDto("SUCCESS", "SUCCESS");
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(FinanceDepositAddEntity balanceSaveEntity, AnxPropertyEntity propertyEntity,
                          FinanceDepositTable balanceTable) throws ServiceException
            , IOException, PayValidatedException {
        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> params = new HashMap<>(10);
        params.put("version", propertyEntity.getVersion());
        params.put("merId", propertyEntity.getMerId());
        params.put("transDate", DateUtil.format("yyyyMMdd", balanceTable.getAddTime()));
        params.put("seqId", orderNo);
        params.put("transTime", DateUtil.format("HHmmss", balanceTable.getAddTime()));
        params.put("amount", String.format("%.2f",
                balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).doubleValue()));
        params.put("notifyUrl", propertyEntity.getNotifyUrl());
        params.put("payType", propertyEntity.getPayType());
        if (!Strings.isNullOrEmpty(balanceSaveEntity.getName())) {
            params.put("buyerName", balanceSaveEntity.getName().replace(" ", ""));
        } else {
            params.put("buyerName", balanceTable.getName().replace(" ", ""));
        }

        params.put("buyerId", String.valueOf(balanceTable.getCId()));

        String sign = getSign(propertyEntity, params);
        params.put("sign", sign);

        Connection.Response response = HttpUtil.getForm(propertyEntity.getBaseUrl(), params);

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

        if (anxDepositEntity == null || Strings.isNullOrEmpty(anxDepositEntity.getCode()) || !anxDepositEntity.getCode().equals("0000")) {

//            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact
//            customer" +
//                    " service.");
            throw new PayValidatedException(anxDepositEntity.getMsg());
        }
        String url = anxDepositEntity.getPayResult();
        log.info(String.format("anx pay,orderNo:%s,url:%s", anxDepositEntity.getSeqId(), url));
        balanceTable.setChannelSerial(anxDepositEntity.getTradeNo());
        return url;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(AnxCallbackEntity callbackEntity) throws Exception {
        AnxPropertyEntity payProperty = getPayProperty(PayConstants.ANX_PAY_KEY, false, AnxPropertyEntity.class);

        String signString = getCallbackSignString(payProperty, callbackEntity);

        log.info(String.format("order no:%s callback result sign compare--callback signString:%s,callback sign :%s"
                , callbackEntity.getSeqId(), signString, callbackEntity.getSign()));
        return SignUtil.verifySignSHA1withRSA(payProperty.getShopPubKey(), signString, callbackEntity.getSign());
    }

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


        String signString = MapUtil.getString(MapUtil.sortByKey(params, false));

        String sign = null;
        try {
//            System.out.println(RSAUtil.encryptByPrivateKey(signString, propertyEntity.getPriKey()));
//
//            System.out.println(RSAUtil.encryptByPrivateKey(signString, propertyEntity.getPriKey(),RSAUtil
//            .KEY_SHA1_WITH_RSA_ALGORITHM));
            sign = SignUtil.signSHA1withRSA(propertyEntity.getPriKey(), signString);
        } catch (Exception e) {
            e.printStackTrace();
            throw ServiceException.exception();
        }

        log.info(String.format("anx request url sign--orderNo:%s,signString:%s,sign:%s", params.get("seqId"),
                signString, sign));

        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getCallbackSignString(AnxPropertyEntity propertyEntity, AnxCallbackEntity callbackEntity) throws ServiceException {
        Map<String, String> params = new HashMap<>(10);
        params.put("version", callbackEntity.getVersion());
        params.put("merId", callbackEntity.getMerId());
        params.put("transDate", callbackEntity.getTransDate());
        params.put("seqId", callbackEntity.getSeqId());
        params.put("amount", callbackEntity.getAmount());
        params.put("stat", callbackEntity.getStat());
        String signString = MapUtil.getString(MapUtil.sortByKey(params, false));

        return signString;
    }
}
