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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.callback.HyPayCallbackEntity;
import com.crm.pay.entity.property.HyPayPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.HyPayService;
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.entity.finance.deposit.FinanceMerchantDepositAddEntity;
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.HAMCSHAUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
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.LinkedHashMap;
import java.util.Map;

/**
 * @program: cwg-my-base
 * @description:
 * @author: houn
 * @create: 2021-05-31 11:45
 */
@Slf4j
@Service
public class HyPayServiceImpl extends BasePayServiceImpl implements HyPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) throws IOException {
        HyPayPropertyEntity entity = new HyPayPropertyEntity();
        entity.setAsyncUrl("https://ad.44a5c8109e4.com/hy/callback");
        entity.setBaseUrl("https://merchant.hy-pay.me");
        entity.setCreateOderUrl("/api/place/orders/checkout");
        entity.setQueryUrl("/api/merchant/orders/query");
        entity.setSecretKey("ptgwrSHxYNTqStWRkpJkVpxutrnMynfH");
        entity.setAccessKey("LmrXlMoQAoGSKFXh");

        System.out.println(AESUtil.encrypt(new ObjectMapper().writeValueAsString(entity),
                "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.decrypt("UMHyBFKRInBpMZ/vdUf7+2hufn5irkB3aRtEK9feQcOsg3uZVMfaat" +
                "/KAJybZs9KScGL3bK4ady5ew78jP1RJwlde8+gP+T4jodU81+kq9jtfhj7KDadPS/gLZdnlbphZDqcHDfmo3JoJus6MqlNIMU" +
                "+c9ARP32pPnR4fshz625P24GLhObu6QF+hZHgn5YPk18bAFxTWKokWGqHHXx8R1THCFgnzm2X7D" +
                "++eJyvWPVQOtWIJb5BBaN68fDSDtiEXbBebnE3nhx66hLVNbhafWg8a+LCrNAO36WpXKrFdhZajywHG9sQb3Q4W3EixvA" +
                "/e5agGvy8+gzWHs5qq5FPDA==", "bfa5559109f94c78af615bcf00d52060"));

        String timestamp = String.valueOf(System.currentTimeMillis());

        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("ACCESS-KEY", "LmrXlMoQAoGSKFXh");
        headers.put("access-timestamp", timestamp);

       /* Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", "PD202511120163415720121_66753851");
        map.put("async_url", "");
        map.put("true_name", "真真");
        map.put("order_amount", "1500");
        map.put("pay_channel", "alipay");*/

        Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", "PD202511120163415720121_66753851");

        String sr = entity.getQueryUrl() + timestamp;
        String signature = HAMCSHAUtil.sha256HMAC(sr, entity.getSecretKey());
        headers.put("ACCESS-SIGN", signature);

        Connection.Response response = HttpUtil.get(entity.getBaseUrl() + entity.getQueryUrl(), headers, map);

        log.info(String.format("url:%s,json:%s,body:%s", entity.getBaseUrl() + entity.getQueryUrl(), JSON.toJSON(map)
                , response.body()));
    }

    /**
     * 请求加密sign
     *
     * @param propertyEntity
     * @param params
     * @return
     * @throws ServiceException
     */
    private static String getSign(HyPayPropertyEntity propertyEntity, Map<String, String> params, String timestamp) {
        String mapString = propertyEntity.getCreateOderUrl() + timestamp + JSON.toJSONString(params);
        try {
            String signature = HAMCSHAUtil.sha256HMAC(mapString, propertyEntity.getSecretKey());
            log.info(String.format("签名:%s,sign string:%s,sign:%s", params.get("mch_no"), mapString, signature));
            return signature;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());
        //通道配置实体
        HyPayPropertyEntity payProperty = getPayProperty(channel, HyPayPropertyEntity.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;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceMerchantDepositAddEntity balanceSaveEntity) throws Exception {
        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());
        //通道配置实体
        HyPayPropertyEntity payProperty = getPayProperty(channel, HyPayPropertyEntity.class);

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

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

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", orderNo);
        map.put("async_url", payProperty.getAsyncUrl());
        map.put("true_name", balanceSaveEntity.getName());
        map.put("order_amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        map.put("pay_channel", balanceSaveEntity.getBankCode());

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

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String callback(HyPayCallbackEntity callbackEntity, String payType) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getMchNo(),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        HyPayPropertyEntity payProperty = getPayProperty(payType, false, HyPayPropertyEntity.class);
        //验证传输数据
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            log.error("hy-pay sign error!");
            return Constants.FAIL;
        }

        //验证传输数据
        int status = PayConstants.CALLBACE_PROCESSING_STATUS;
        if (PayConstants.HY_SUCCESS_RESULT.equals(callbackEntity.getStatus())) {
            status = PayConstants.CALLBACE_SUCCESS_STATUS;
        } else if (PayConstants.HY_CANCEL_RESULT.equals(callbackEntity.getStatus())) {
            status = PayConstants.CALLBACE_FAIL_STATUS;
        }

        String timestamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("ACCESS-KEY", payProperty.getAccessKey());
        headers.put("access-timestamp", timestamp);

        Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", callbackEntity.getMchNo());

        String sr = payProperty.getQueryUrl() + timestamp;
        String signature = HAMCSHAUtil.sha256HMAC(sr, payProperty.getSecretKey());
        headers.put("ACCESS-SIGN", signature);

        Connection.Response response = HttpUtil.get(payProperty.getBaseUrl() + payProperty.getQueryUrl(), headers, map);
        log.info(String.format("url:%s,json:%s,body:%s", payProperty.getBaseUrl() + payProperty.getQueryUrl(),
                JSON.toJSON(map), response.body()));

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (200 != response.statusCode() || requestEntity == null) {
            log.error("callback error! orderNo:{}", callbackEntity.getMchNo());
            return Constants.FAIL;
        }
        JSONObject jsonData = JSONObject.parseObject(requestEntity.getString("data"));
        if (jsonData == null) {
            return Constants.FAIL;
        }
        if (!callbackEntity.getStatus().equals(jsonData.getString("status")) || new BigDecimal(jsonData.getString(
                "real_pay_amount")).compareTo(new BigDecimal(callbackEntity.getAmount())) != 0) {
            return Constants.FAIL;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getMchNo(), callbackEntity.getMchNo(),
                        status, callbackEntity);
        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getAmount()));
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
        return Constants.SUCCESS;
    }

    private String getUrl(HyPayPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          FinanceDepositAddEntity balanceSaveEntity) throws ServiceException, IOException,
            PayValidatedException {
//        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
//        String timestamp = String.valueOf(balanceTable.getAddTime().getTime() / 1000);
//
//        Map<String, String> headers = new HashMap<>();
//        headers.put("Content-Type", "application/json");
//        headers.put("ACCESS-KEY", propertyEntity.getAccessKey());
//        headers.put("access-timestamp", timestamp);
//
//        Map<String, String> map = new LinkedHashMap<>();
//        map.put("mch_no", orderNo);
//        map.put("async_url", propertyEntity.getAsyncUrl());
//        map.put("true_name", balanceTable.getName());
//        map.put("order_amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
//        map.put("pay_channel", balanceSaveEntity.getBankCode());
//
//        String sign = getSign(propertyEntity, map, timestamp);
//        headers.put("ACCESS-SIGN", sign);
//
//        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getCreateOderUrl(),
//                headers, map);
//        log.info(String.format("url:%s,json:%s,body:%s",
//                propertyEntity.getBaseUrl() + propertyEntity.getCreateOderUrl(), JSON.toJSON(map), response.body()));
//
//        JSONObject requestEntity = JSON.parseObject(response.body());
//        if (200 != response.statusCode() || requestEntity == null || 0 != requestEntity.getInteger("code")) {
//            String msg = requestEntity.getString("msg");
//            if (!Strings.isNullOrEmpty(msg)) {
//                throw new PayValidatedException(msg);
//            } else {
//                throw new PayValidatedException("Payment platform is abnormal, please try again later or contact " +
//                        "customer service.");
//            }
//        }
//
//        String url = requestEntity.getJSONObject("data").getString("url");
//        if (Strings.isNullOrEmpty(url)) {
//            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact
//            customer" +
//                    " service.");
//        }
//        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("mch_no"), url));

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", orderNo);
        map.put("async_url", propertyEntity.getAsyncUrl());
        map.put("true_name", balanceTable.getName());
        map.put("order_amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        map.put("pay_channel", balanceSaveEntity.getBankCode());
        return getUrl(propertyEntity, balanceTable, map);
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(HyPayPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          Map<String, String> map) throws ServiceException, IOException,
            PayValidatedException {
        String timestamp = String.valueOf(balanceTable.getAddTime().getTime() / 1000);

        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("ACCESS-KEY", propertyEntity.getAccessKey());
        headers.put("access-timestamp", timestamp);

        String sign = getSign(propertyEntity, map, timestamp);
        headers.put("ACCESS-SIGN", sign);

        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getCreateOderUrl(),
                headers, map);
        log.info(String.format("url:%s,json:%s,body:%s",
                propertyEntity.getBaseUrl() + propertyEntity.getCreateOderUrl(), JSON.toJSON(map), response.body()));

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (200 != response.statusCode() || requestEntity == null || 0 != requestEntity.getInteger("code")) {
            String msg = requestEntity.getString("msg");
            if (!Strings.isNullOrEmpty(msg)) {
                throw new PayValidatedException(msg);
            } else {
                throw new PayValidatedException("Payment platform is abnormal, please try again later or contact " +
                        "customer service.");
            }
        }

        String url = requestEntity.getJSONObject("data").getString("url");
        if (Strings.isNullOrEmpty(url)) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("mch_no"), url));
        return url;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(HyPayCallbackEntity callbackEntity, HyPayPropertyEntity payProperty) throws Exception {
        String _sign = callbackEntity.getSign();
        String sign = getSign(callbackEntity, payProperty);

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

    /**
     * 回调请求加密sign
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(HyPayCallbackEntity callbackEntity, HyPayPropertyEntity payProperty) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("mch_no", callbackEntity.getMchNo());
        map.put("amount", callbackEntity.getAmount());
        map.put("status", callbackEntity.getStatus());
        String mapString = JSON.toJSONString(map) + callbackEntity.getTimestamp();
        try {
            String signature = HAMCSHAUtil.sha256HMAC(mapString, payProperty.getSecretKey());
            log.info(String.format("签名:%s,sign string:%s,sign:%s", map.get("mch_no"), mapString, signature));
            return signature;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
