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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.property.BieasePropertyEntity;
import com.crm.pay.service.BieaseService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
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.*;
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.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2024-03-25 11:45
 */
@Slf4j
@Service
public class BieaseServiceImpl extends BasePayServiceImpl implements BieaseService {

    @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.BIEASE_PAY_TYPE_KEY);
        //通道配置实体
        BieasePropertyEntity payProperty = getPayProperty(channel, BieasePropertyEntity.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(Map<String, String> callbackEntity) throws Exception {

        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.get("apiOrderId"),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        BieasePropertyEntity payProperty = getPayProperty(PayConstants.BIEASE_PAY_TYPE_KEY, false,
                BieasePropertyEntity.class);

        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw ServiceException.exception("sign error");
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.BIEASE_SUCCESS_STATUS,
                        callbackEntity.get("apiOrderId"),
                        callbackEntity.get("tradeId"),
                        callbackEntity.get("tradeStatus"), callbackEntity);

        BigDecimal amount = new BigDecimal(callbackEntity.get("amount"));

        balanceCallbackUpdateEntity.setTransformAmount(amount);

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

    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(BieasePropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          FinanceDepositAddEntity balanceSaveEntity) throws ServiceException, IOException,
            PayValidatedException {
        String datetime = DateUtil.convertDateBetweenTimeZone("GMT+8", new Date());
        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> params = new HashMap<>(10);
        params.put("appId", propertyEntity.getAppId());
        params.put("apiUserId", String.valueOf(balanceTable.getCId()));
        params.put("amount",
                balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).stripTrailingZeros().toPlainString());
//        params.put("amount", balanceTable.getAmount().stripTrailingZeros().toPlainString());
        params.put("apiAmountType", propertyEntity.getApiAmountType());
        params.put("legaltenderid", propertyEntity.getLegaltenderid());
        params.put("apiOrderType", propertyEntity.getApiOrderType());
        params.put("apiOrderId", orderNo);
        params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("notifyUrl", propertyEntity.getNotifyUrl());
        params.put("paymentName", balanceTable.getName());

        String sign = getSign(propertyEntity, params);

        params.put("_sign", sign);
        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/json");

        String payUrl = propertyEntity.getBaseUrl() + propertyEntity.getPayUrl();

        Connection.Response response = HttpUtil.post(
                payUrl, headers, params);

        log.info(String.format("url:%s,json:%s,body:%s", payUrl,
                JSON.toJSON(params), response.body()));
        JSONObject requestEntity = JSON.parseObject(response.body());

        if (requestEntity == null
                || (requestEntity.get("code") != null && !requestEntity.get("code").equals(200))) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        String url = String.valueOf(requestEntity.getJSONObject("result").get("url"));
        log.info(String.format(",orderNo:%s,url:%s", params.get("apiOrderId"), url));
        return url;
    }

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

        String _sign = callbackEntity.get("_sign");
        callbackEntity.remove("_sign");
        String sign = getCallbackSign(payProperty, callbackEntity);

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

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

        String signString = MapUtil.getStringSortByKey(params) + "&key=" + propertyEntity.getSecretKey();
        String sign = MD5Util.getMD5(signString);
        String order = params.get("apiOrderId");
        if (Strings.isNullOrEmpty(order)) {
            log.info(String.format("biease--sign string:%s,sign:%s", signString, sign));
        } else {
            log.info(String.format("orderNo:%s,sign string:%s,sign:%s", order, signString, sign));
        }


        return sign;
    }

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

        String signString = MapUtil.getStringSortByKey(callbackEntity) + "&key=" + propertyEntity.getCallbackKey();
        String sign = MD5Util.getMD5(signString);

        log.info(String.format("callback--orderNo:%s,callback string:%s,sign:%s", callbackEntity.get("apiOrderId"),
                signString,
                sign));

        return sign;
    }
}
