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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dto.callback.click.ClickPayResultDto;
import com.crm.pay.entity.callback.ClickPayCallbackEntity;
import com.crm.pay.entity.property.ClickPayPropertyEntity;
import com.crm.pay.service.ClickPayService;
import com.crm.pay.service.FinanceDepositService;
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.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.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
import com.crm.rely.backend.util.UUIDUtil;
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.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2022-03-07 11:45
 */
@Slf4j
@Service
public class ClickPayServiceImpl extends BasePayServiceImpl implements ClickPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) {
        System.out.println(AESUtil.decrypt("", "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.encrypt("{\n" +
                "    \"merchantId\":\"968279\",\n" +
                "    \"key\":\"MZ2W4ZDJNZTUAY3XM5WWC4TLMV2HGLTDN5WTQMRWGE4Q\",\n" +
                "    \"callbackKey\":\"MZ2W4ZDJNZTUAY3XM5WWC4TLMV2HGLTDN5WTEOBUGU2Q\",\n" +
                "    \"baseUrl\":\"https://www.sign123.co/merchantapi/Order/RemoteSignatureVerificationPostV2\",\n" +
                "    \"pickupUrl\":\"https://pay.cwgvu.com/click/pickup\"\n" +
                "}", "bfa5559109f94c78af615bcf00d52060"));
    }

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

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.CLICK_PAY_KEY);
        //通道配置实体
        ClickPayPropertyEntity payProperty = getPayProperty(channel, ClickPayPropertyEntity.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 ClickPayResultDto callback(ClickPayCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getApiOrderNo(), JSON.toJSONString(callbackEntity)));

        boolean flag = validate(callbackEntity);
        ClickPayResultDto resultDto = new ClickPayResultDto();
        if (!flag) {
            resultDto.setCode("-1");
            resultDto.setMessage("3");
            resultDto.setData("fail");
            resultDto.setSuccess("false");
            return resultDto;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.CLICKPAY_STATUS,
                        callbackEntity.getApiOrderNo(),
                        callbackEntity.getOrderID(),
                        callbackEntity.getOrderStatus(),callbackEntity);

        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getMoney()));
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
        //返回三方数据
        resultDto.setCode(callbackEntity.getOrderStatus());
        resultDto.setMessage(callbackEntity.getOrderStatus());
        resultDto.setData(callbackEntity.getOrderStatus());
        resultDto.setSuccess("true");
        return resultDto;
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(ClickPayPropertyEntity propertyEntity, FinanceDepositTable balanceTable, FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        Map<String, String> params = new HashMap<>(10);
        params.put("apiOrderNo", String.format("%s_%d", balanceTable.getSerial(), balanceTable.getLogin()));
        params.put("money",String.format("%.0f", balanceTable.getTransformAmount().setScale(0, RoundingMode.HALF_UP).doubleValue()));
        params.put("uid", propertyEntity.getMerchantId());
        params.put("uniqueCode", UUIDUtil.getUUID());
        params.put("payType", "1");
        params.put("PayerName", balanceTable.getName());
        params.put("JumpUri", propertyEntity.getPickupUrl());
        String sign = getSign(propertyEntity, params);

        params.put("signature", 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(),
                JSON.toJSONString(params)));
        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl(), 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.getString("code").equals("0") || !jsonObject.getString("success").equals("true")) {
            throw new ServiceException(jsonObject.getString("message"));
        }
        String url = jsonObject.getString("data");
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", params.get("apiOrderNo"), url));
        return url;
    }

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

        String signString = String.format("apiOrderNo=%s&money=%s&OrderID=%s&OrderStatus=%s&uniqueCode=%s&key=%s",
                callbackEntity.getApiOrderNo(), callbackEntity.getMoney(), callbackEntity.getOrderID(),
                callbackEntity.getOrderStatus(), callbackEntity.getUniqueCode(),
                payProperty.getCallbackKey());

        String sign = MD5Util.getMD5(signString);

        log.info(String.format("click callback--orderNo:%s,signString:%s,sign:%s", callbackEntity.getApiOrderNo(),
                signString
                , sign));

        return sign.equals(callbackEntity.getSignature());
    }

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

        params.remove("signature");

        String signString = String.format("apiOrderNo=%s&money=%s&uid=%s&uniqueCode=%s&payType=%s&key=%s", params.get(
                        "apiOrderNo"), params.get("money"), params.get("uid"), params.get("uniqueCode"),
                params.get("payType"), propertyEntity.getKey());
        String sign = MD5Util.getMD5(signString);

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

        return sign;
    }

}
