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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dao.repository.CustomInfoRepository;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.callback.UenjoyCallbackEntity;
import com.crm.pay.entity.property.UenjoyPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.UenjoyService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
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.CustomInfoTable;
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.math.BigDecimal;
import java.net.URLEncoder;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2021-09-08 11:45
 */
@Slf4j
@Service
public class UenjoyServiceImpl extends BasePayServiceImpl implements UenjoyService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private CustomInfoRepository customInfoRepository;

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

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.UENJOY_PAY_KEY);
        //通道配置实体
        UenjoyPropertyEntity payProperty = getPayProperty(channel, UenjoyPropertyEntity.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 void callback(UenjoyCallbackEntity 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.UENJOY_SUCCESS_STATUS,
                        callbackEntity.getMerchantOrderNo(),
                        callbackEntity.getOrderNo(),
                        callbackEntity.getStatus(),callbackEntity);

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

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(UenjoyPropertyEntity propertyEntity, FinanceDepositTable balanceTable,FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        long timestamp = getTimestamp(propertyEntity);
        String timestampString = DateUtil.format("yyyy-MM-dd'T'HH:mm:ss", new Date(timestamp));

        CustomInfoTable customInfoTable = customInfoRepository.findFirstById(balanceTable.getCustomId());
        StringBuffer nameSbuff = new StringBuffer();
        if (!Strings.isNullOrEmpty(customInfoTable.getFirstName())) {
            nameSbuff.append(customInfoTable.getFirstName());
        }
        if (!Strings.isNullOrEmpty(customInfoTable.getLastName())) {
            nameSbuff.append(customInfoTable.getLastName());
        }

        String name = nameSbuff.toString();

        Map<String, String> params = new HashMap<>(10);
        params.put("apiKey", propertyEntity.getApiKey());
        params.put("timestamp", timestampString);
        params.put("signVersion", propertyEntity.getSignVersion());
        params.put("currency", balanceTable.getCurrency());
        params.put("legalCurrency", balanceTable.getTransformCurrency());
        params.put("userAreaCode", customInfoTable.getAreaCode());
        params.put("userMobile", customInfoTable.getPhone());
        params.put("kycLevel", Strings.isNullOrEmpty(propertyEntity.getKycLevel())?"0":propertyEntity.getKycLevel());
        params.put("kycName", name);
//        params.put("kycIdType", "1");
//        params.put("kycIdNo", customInfoTable.getIdentity());
        params.put("redirectUrl", propertyEntity.getPickupUrl());
        params.put("callbackUrl", propertyEntity.getNotifyUrl());
        params.put("orderNo", String.format("%s_%d", balanceTable.getSerial(), balanceTable.getLogin()));
        params.put("coinCount", String.format("%.2f", balanceTable.getAmount().doubleValue()));

        String sign = getSign(propertyEntity, params);

        params.put("sign", sign);
        Map<String, String> header = new HashMap<>(1);

        header.put("Content-Type", "application/json;charset=UTF-8");

        log.info(String.format("send rul:%s,params:%s", propertyEntity.getBaseUrl() + propertyEntity.getBuyUrl(),
                JSON.toJSONString(params)));
        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getBuyUrl(), header,
                JSON.toJSON(params));
        String body = response.body();
        log.info(String.format("get url body:%s", body));
        if (Strings.isNullOrEmpty(body)) {
            throw new ServiceException(Constants.SYSTEM_ERROR);
        }
        JSONObject jsonObject = JSON.parseObject(body);

        if (jsonObject == null || jsonObject.size() <= 0) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer service.");
        }
        if (!jsonObject.getInteger("code").equals(0)) {
            throw new ServiceException(jsonObject.getString("msg"));
        }
        if (!Strings.isNullOrEmpty(balanceSaveEntity.getPhone()) || !Strings.isNullOrEmpty(balanceSaveEntity.getAreaCode())) {
            if (!Strings.isNullOrEmpty(balanceSaveEntity.getPhone())) {
                customInfoTable.setPhone(balanceSaveEntity.getPhone());
            }
            if (!Strings.isNullOrEmpty(balanceSaveEntity.getAreaCode())) {
                customInfoTable.setAreaCode(balanceSaveEntity.getAreaCode());
            }
            customInfoRepository.save(customInfoTable);
        }
        String url = jsonObject.getJSONObject("data").getString("url");
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", params.get("orderNo"), url));
        return url;
    }

    /**
     * 获取时间戳
     *
     * @param propertyEntity
     * @return
     * @throws Exception
     */
    private long getTimestamp(UenjoyPropertyEntity propertyEntity) throws Exception {

        Connection.Response response = HttpUtil.get(propertyEntity.getBaseUrl() + propertyEntity.getTimestampUrl());

        String body = response.body();
        log.info(String.format("get timestamp body:%s", body));
        if (Strings.isNullOrEmpty(body)) {
            throw new ServiceException(Constants.SYSTEM_ERROR);
        }
        JSONObject jsonObject = JSON.parseObject(body);

        if (jsonObject == null || jsonObject.size() <= 0) {
            throw new ServiceException(Constants.SYSTEM_ERROR);
        }
        if (!jsonObject.getInteger("code").equals(0)) {
            throw new ServiceException(jsonObject.getString("msg"));
        }
        long timestamp = jsonObject.getJSONObject("data").getLong("timestamp");
        return timestamp;
    }

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

        Map<String, String> params = new TreeMap();

        params.put("apiKey", callbackEntity.getApiKey());
        params.put("timestamp", callbackEntity.getTimestamp());
        params.put("signVersion", callbackEntity.getSignVersion());
        params.put("sign", callbackEntity.getSign());
        params.put("orderNo", callbackEntity.getOrderNo());
        params.put("merchantOrderNo", callbackEntity.getMerchantOrderNo());
        params.put("status", callbackEntity.getStatus());
        params.put("currency", callbackEntity.getCurrency());
        params.put("side", callbackEntity.getSide());
        params.put("count", callbackEntity.getCount());
        params.put("realAmount", callbackEntity.getRealAmount());
        params.put("tradeTime", callbackEntity.getTradeTime());

        String _sign = callbackEntity.getSign();

        String sign = getSign(payProperty, params);

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

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

        params.remove("sign");
        Map<String, String> params1 = new HashMap<>(3);
        params1.put("apiKey", propertyEntity.getApiKey());
        params1.put("timestamp", params.get("timestamp"));
        params1.put("signVersion", propertyEntity.getSignVersion());

        //构建字符串
        String mapString = MapUtil.getString(MapUtil.sortByKey(params1));

        //URL编码
        String mapEncoderString = URLEncoder.encode(mapString);

        //对生成的字符串使用HMAC-SHA256进行加密
        String signHMACSHA256 = HAMCSHAUtil.sha256HMACBase64(mapEncoderString, propertyEntity.getApiSecret());

        //进行base64
        String baseString = Base64Util.toBase64String(signHMACSHA256);

        //再次进行URL编码
        String sign = URLEncoder.encode(baseString);

        log.info(String.format("加密处理--请求orderNo:%s,sign string:%s,sign:%s", params.get("orderNo"), mapEncoderString,
                sign));

        return sign;
    }
}
