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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.callback.long77.Long77CallbackEntity;
import com.crm.pay.entity.property.Long77PayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.Long77PayService;
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.HashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2022-08-17 11:45
 */
@Slf4j
@Service
public class Long77PayServiceImpl extends BasePayServiceImpl implements Long77PayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) {
//        System.out.println(AESUtil.decrypt("", "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.encrypt("{\n" +
                "\t\"partnerId\":\"10164\",\n" +
                "\t\"partnerSecret\":\"2sB5KA0D0t7js1au0PH8fI4x\",\n" +
                "\t\"callbackUrl\":\"https://pay.cwg-fx.com/long77/callback\",\n" +
                "\t\"pickupUrl\":\"https://pay.cwg-fx.com/long77/pickup\",\n" +
                "\t\"baseUrl\":\"https://vi.long77.net/gateway/bnb/createVA.do\",\n" +
                "}", "bfa5559109f94c78af615bcf00d52060"));
    }

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

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

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

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

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

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(Long77CallbackEntity callbackEntity) throws Exception {

        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getPartnerOrderCode(),
                JSON.toJSONString(callbackEntity)));

        //获取配置
        Long77PayPropertyEntity payProperty = getPayProperty(PayConstants.LONG77_VA_PAY_KEY, false,
                Long77PayPropertyEntity.class);

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

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.LONG77_SUCCESS_STATUS,
                        callbackEntity.getPartnerOrderCode(),
                        callbackEntity.getSystemOrderCode(),
                        callbackEntity.getPayment().getStatus(), callbackEntity);

        BigDecimal amount = new BigDecimal(callbackEntity.getAmount());

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

    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(Long77PayPropertyEntity propertyEntity, FinanceDepositTable balanceTable) throws ServiceException, IOException,
            PayValidatedException {

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> params = new HashMap<>(10);
        params.put("partner_order_code", orderNo);
        params.put("timestamp", DateUtil.getTimestamp());
        params.put("random", GetRandom.getRandom());
        params.put("partner_id", propertyEntity.getPartnerId());
        params.put("amount", balanceTable.getTransformAmount().stripTrailingZeros().toPlainString());
        params.put("notify_url", propertyEntity.getCallbackUrl());
        params.put("return_url", propertyEntity.getPickupUrl());

        String sign = getSign(propertyEntity, params);

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

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

        log.info(String.format("long 77 url:%s,json:%s,body:%s", propertyEntity.getBaseUrl(),
                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("data").get("payment_url"));
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", params.get("partner_order_code"), url));
        return url;
    }

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

        String _sign = callbackEntity.getSign();

        String sign = getCallbackSign(payProperty, callbackEntity);

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

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

        //partner_id:timestamp:random:partner_order_code:amount:customer_name:payee_ name:notify_url:
        // return_url:extra_data:partner_secret
        String signString = String.format(
                "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s:%s",
                params.getOrDefault("partner_id", ""),
                params.getOrDefault("timestamp", ""),
                params.getOrDefault("random", ""),
                params.getOrDefault("partner_order_code", ""),
                params.getOrDefault("amount", ""),
                params.getOrDefault("customer_name", ""),
                params.getOrDefault("payee_name", ""),
                params.getOrDefault("notify_url", ""),
                params.getOrDefault("return_url", ""),
                params.getOrDefault("extra_data", ""),
                propertyEntity.getPartnerSecret()
        );
        String sign = MD5Util.getMD5(signString);
        String order = params.get("partner_order_code");
        if (Strings.isNullOrEmpty(order)) {
            log.info(String.format("long 77 pay--signString:%s,sign:%s", signString, sign));
        } else {
            log.info(String.format("long 77 pay--orderNo:%s,signString:%s,sign:%s", order, signString, sign));
        }


        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getCallbackSign(Long77PayPropertyEntity propertyEntity, Long77CallbackEntity callbackEntity) {

        //md5(partner_id:system_order_code:partner_order_code:channel_code:
        // amount:request_time:extra_data:payment_id:paid_amount:fees:payment_time:
        // bank_code:bank_account_no:bank_account_name:callback_time:status:partner_secret)
        String signString = String.format(
                "%s:%s:%s:%s:" +
                        "%s:%s:%s:%s:%s:%s:" +
                        "%s:%s:%s:%s:%s:" +
                        "%s:%s",
                callbackEntity.getPartnerId(),
                callbackEntity.getSystemOrderCode(),
                callbackEntity.getPartnerOrderCode(),
                callbackEntity.getChannelCode(),
                callbackEntity.getAmount(),
                callbackEntity.getRequestTime(),
                callbackEntity.getExtraData(),
                callbackEntity.getPayment().getPaymentId(),
                callbackEntity.getPayment().getPaidAmount(),
                callbackEntity.getPayment().getFees(),
                callbackEntity.getPayment().getPaymentTime(),
                callbackEntity.getPayment().getBankCode(),
                callbackEntity.getPayment().getBankAccountNo(),
                callbackEntity.getPayment().getBankAccountName(),
                callbackEntity.getPayment().getCallbackTime(),
                callbackEntity.getPayment().getStatus(),
                propertyEntity.getPartnerSecret()
        );
        String sign = MD5Util.getMD5(signString);

        log.info(String.format("long 77 callback--orderNo:%s,signString:%s,sign:%s", callbackEntity.getPartnerOrderCode(),
                signString,
                sign));

        return sign;
    }
}
