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

import com.alibaba.fastjson.JSON;
import com.crm.pay.dto.callback.otc365.cn.Otc365CnCallbackResponseDataDto;
import com.crm.pay.dto.callback.otc365.cn.Otc365CnCallbackResponseDto;
import com.crm.pay.entity.otc365.cn.Otc365CnOrderEntity;
import com.crm.pay.entity.otc365.cn.Otc365CnResponseEntity;
import com.crm.pay.entity.property.Otc365CnPayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.Otc365CnV2Service;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.entity.custom.info.CustomInfoEntity;
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.*;
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.math.RoundingMode;
import java.text.ParseException;
import java.util.LinkedHashMap;
import java.util.Map;

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

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2021-01-21 11:45
 */
@Slf4j
@Service
public class Otc365CnV2ServiceImpl extends BasePayServiceImpl implements Otc365CnV2Service {

    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) {
        System.out.println(AESUtil.encrypt("", "bfa5559109f94c78af615bcf00d52060"));

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

    /**
     * 支付请求加密sign
     *
     * @param orderEntity
     * @return
     * @throws ServiceException
     */
    private static String getSignString(Otc365CnPayPropertyEntity propertyEntity, Otc365CnOrderEntity orderEntity) {


        Map<String, String> map = new LinkedHashMap<>(14);
        map.put("companyId", String.valueOf(orderEntity.getCompanyId()));
        map.put("kyc", orderEntity.getKyc());
        map.put("username", orderEntity.getUsername());
        map.put("phone", orderEntity.getPhone());
        map.put("orderType", String.valueOf(orderEntity.getOrderType()));
        map.put("companyOrderNum", orderEntity.getCompanyOrderNum());
        map.put("coinSign", orderEntity.getCoinSign());
        map.put("payCoinSign", orderEntity.getPayCoinSign());
        map.put("coinAmount", orderEntity.getCoinAmount().stripTrailingZeros().toPlainString());
        map.put("total", orderEntity.getTotal().stripTrailingZeros().toPlainString());
        map.put("orderTime", orderEntity.getOrderTime());
        map.put("syncUrl", orderEntity.getSyncUrl());
        map.put("asyncUrl", orderEntity.getAsyncUrl());
        if (orderEntity.getOrderPayChannel() != null) {
            map.put("orderPayChannel", String.valueOf(orderEntity.getOrderPayChannel()));
        }

//        map.put("secretKey", propertyEntity.getSecretKey());

        String signString = MapUtil.getString(MapUtil.sortByKey(map, false));
        String sign = SignUtil.signSHA256withRSA(propertyEntity.getPri(), signString);
//        String sign = MD5Util.getMD5(signString);

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

        return sign;
    }

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());
        //通道配置实体
        Otc365CnPayPropertyEntity payProperty = getPayProperty(channel, Otc365CnPayPropertyEntity.class);

        balanceSaveEntity.setCurrency(channel.getCurrency());

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

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

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Otc365CnCallbackResponseDto callback(Map<String, String> callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.get("companyOrderNum"),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        Otc365CnPayPropertyEntity payProperty = getPayProperty(PayConstants.OTC_365_CN_V2_PAY_KEY, false,
                Otc365CnPayPropertyEntity.class);

        Otc365CnCallbackResponseDto responseDto = new Otc365CnCallbackResponseDto();

        Otc365CnCallbackResponseDataDto dataDto = new Otc365CnCallbackResponseDataDto();
        dataDto.setOtcOrderNum(callbackEntity.get("otcOrderNum"));
        dataDto.setCompanyOrderNum(callbackEntity.get("companyOrderNum"));

        responseDto.setData(dataDto);
        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            responseDto.setCode(500);
            responseDto.setMsg("fail");
            responseDto.setSuccess(false);
            return responseDto;
        }
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(
                        PayConstants.OTC_365_CN_SUCCESS_CHECK,
                        callbackEntity.get("companyOrderNum"),
                        callbackEntity.get("otcOrderNum"),
                        callbackEntity.get("tradeStatus"), callbackEntity);
        if (PayConstants.CALLBACE_SUCCESS_STATUS.equals(balanceCallbackUpdateEntity.getCallbackStatus())) {

            balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.get("total")));
        }
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
        responseDto.setCode(200);
        responseDto.setMsg("success");
        responseDto.setSuccess(true);

        return responseDto;
    }

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

        String timeImpl = String.valueOf(DateUtil.getTimestampByChina());
        Otc365CnOrderEntity otc365CnOrderEntity = new Otc365CnOrderEntity();
        otc365CnOrderEntity.setCoinAmount(balanceTable.getAmount().setScale(0, RoundingMode.HALF_UP));
        otc365CnOrderEntity.setCoinSign(propertyEntity.getCoinSign());
        otc365CnOrderEntity.setCompanyId(propertyEntity.getCompanyId());
        otc365CnOrderEntity.setCompanyOrderNum(balanceTable.getSerial() + "_" + balanceTable.getLogin());
        otc365CnOrderEntity.setKyc(propertyEntity.getKyc());
        otc365CnOrderEntity.setOrderTime(timeImpl);
        otc365CnOrderEntity.setOrderType(propertyEntity.getOrderType());
        otc365CnOrderEntity.setPayCoinSign(balanceTable.getTransformCurrency());
        otc365CnOrderEntity.setTotal(balanceTable.getTransformAmount().setScale(0, RoundingMode.HALF_UP));
        otc365CnOrderEntity.setPhone(infoEntity.getPhone());
        otc365CnOrderEntity.setSyncUrl(propertyEntity.getPickupUrl());
        otc365CnOrderEntity.setAsyncUrl(propertyEntity.getCallbackUrl());
        otc365CnOrderEntity.setOrderPayChannel(Integer.valueOf(balanceTable.getBankCode()));
        if (Strings.isNullOrEmpty(balanceSaveEntity.getName())) {
            otc365CnOrderEntity.setUsername(infoEntity.getName());
        } else {
            otc365CnOrderEntity.setUsername(balanceSaveEntity.getName());
        }

        String sign = getSignString(propertyEntity, otc365CnOrderEntity);

        otc365CnOrderEntity.setSign(sign);

        String data = JSON.toJSONString(otc365CnOrderEntity);
        log.info(String.format("otc365 v2 send address:%s,request:%s",
                propertyEntity.getBaseUrl() + propertyEntity.getAddOrder(),
                data));
        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getAddOrder(), data);
        log.info(String.format("otc365 v2 Response:%s", response.body()));
        Otc365CnResponseEntity otc365CnResponseEntity = parseObject(response.body(), Otc365CnResponseEntity.class);

        if (otc365CnResponseEntity == null
                || otc365CnResponseEntity.getCode() == null
                || otc365CnResponseEntity.getSuccess() == null
                || !otc365CnResponseEntity.getCode().equals(200)
                || otc365CnResponseEntity.getSuccess() != true) {
            if (otc365CnResponseEntity != null) {
                throw new PayValidatedException(otc365CnResponseEntity.getMsg());
            }

            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        String url = otc365CnResponseEntity.getData().getLink();
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", otc365CnOrderEntity.getCompanyOrderNum(), url));
        return url;
    }

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

        String _sign = callbackEntity.get("sign");

        String signString = getSignString(payProperty, callbackEntity);
//        String sign = SignUtil.verifySign(propertyEntity.getPri(), signString);
        boolean verify = SignUtil.verifySignSHA256withRSA(payProperty.getPub(), signString, _sign);
        log.info(String.format("order no:%s,callback result sign compare--callback sign:%s",
                callbackEntity.get("companyOrderNum"), _sign));
        return verify;
    }

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

        map.remove("sign");
        String signString = MapUtil.getString(MapUtil.sortByKey(map, false));


        return signString;
    }


}

