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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawCwgAliDto;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawCwgDto;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawCwgUnioniDto;
import com.crm.pay.entity.property.CwgPayPropertyEntity;
import com.crm.pay.entity.property.CwgWithdrawPropertyEntity;
import com.crm.pay.service.CwgPayService;
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.dto.finance.withdraw.FinanceWithdrawDto;
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.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawCwgEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawInfoEntity;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.FinanceWithdrawTable;
import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
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.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2022-07-13 11:45
 */
@Slf4j
@Service
public class CwgPayServiceImpl extends BasePayServiceImpl implements CwgPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    public static void main(String[] args) {
        System.out.println(AESUtil.encrypt("{\n" +
                "    \"shopId\":\"22047\",\n" +
                "    \"secretKey\":\"6b53684807d3415c8863b68fcf5daa15\",\n" +
                "    \"callbackUrl\":\"https://pay.cwgvu.com/cwg/withdraw/callback\",\n" +
                "    \"agentCallbackUrl\":\"https://pay.cwgvu.com/cwg/agent/withdraw/callback\",\n" +
                "    \"payoutUrl\":\"https://ad.igopays.com/pay/order/withdraw/create\",\n" +
                "    \"withdrawType\":2\n" +
                "}", "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.decrypt("GB3aLag3RTGor/IEzk332AJI4D/pd" +
                        "/pZuNdj9yJXAyvIRChMgAoUFJfHFxexgpBvEXkOS9BQHyizY2Fu/QLJCjo" +
                        "/xYRU6lFD3Y9dfmZ1nNuvffVkpNxnqUF19Ox14tmvs+BdLN57mluI5ga/fdO0UNb2PjtUFBA+9qXu" +
                        "++hTSDjf6KIcAVmGntIb8yswgDpBXJWeFec55IBexZnXOrij6rPG1oKAdDyRrxPbVws7uSYWibLSx4DmXDLOI5LxKVOEG1BRs7Ilz0nl44JLVCJ12w4oEBmQwzykCpCCVEzWo7kdo9zzWeBtCaa9WnEv0M9f\n",
                "bfa5559109f94c78af615bcf00d52060"));
    }

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

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

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

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

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        String datetime = DateUtil.convertDateBetweenTimeZone("GMT+0", new Date());
        Map<String, String> params = new HashMap<>(10);
        params.put("shopOrderNo", orderNo);
        params.put("shopId", payProperty.getShopId());
        params.put("amount",
                balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).stripTrailingZeros().toPlainString());
//        params.put("amount", balanceTable.getAmount().stripTrailingZeros().toPlainString());
        params.put("callbackUrl", payProperty.getCallbackUrl());
        params.put("pickupUrl", payProperty.getPickupUrl());
        params.put("name", balanceTable.getName());
        params.put("requestTime", datetime);
        params.put("payType", balanceTable.getBankCode());

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

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

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

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

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

        String amount =
                balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).stripTrailingZeros().toPlainString();


        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        String datetime = DateUtil.convertDateBetweenTimeZone("GMT+0", new Date());
        Map<String, String> params = new HashMap<>(10);
        params.put("shopOrderNo", orderNo);
        params.put("shopId", payProperty.getShopId());
        params.put("amount", amount);
//        params.put("amount", balanceTable.getAmount().stripTrailingZeros().toPlainString());
        params.put("callbackUrl", payProperty.getCallbackUrl());
        params.put("pickupUrl", payProperty.getPickupUrl());
        params.put("name", balanceSaveEntity.getName());
        params.put("requestTime", datetime);
        params.put("payType", balanceTable.getBankCode());

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

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


        callback(callbackEntity, PayConstants.CWG_PAY_KEY);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(Map<String, String> callbackEntity, String payType) throws Exception {
        Map<String, Object> mapResult = new HashMap<>(4);
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.get("shopOrderNo"),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        CwgPayPropertyEntity payProperty = getPayProperty(payType, false, CwgPayPropertyEntity.class);

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

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.CWG_SUCCESS_STATUS,
                        callbackEntity.get("shopOrderNo"),
                        callbackEntity.get("orderNo"),
                        callbackEntity.get("status"), callbackEntity);

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

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

    /**
     * 获取支付url
     *
     * @param
     * @return
     * @throws ServiceException
     */
    private String getUrl(CwgPayPropertyEntity propertyEntity,
                          Map<String, String> params) throws ServiceException, IOException,
            PayValidatedException {


        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("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.get("data"));
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", params.get("shopOrderNo"), url));
        return url;
    }


    @Override
    public FinanceWithdrawCwgDto withdrawInfo(FinanceWithdrawInfoEntity entity) throws Exception {
        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());
        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }

        SysRemitChannelTable withdrawChannel = getWithdrawChannel(entity.getPayType());
        CwgWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawPayProperty(withdrawChannel,
                CwgWithdrawPropertyEntity.class);

        if (proxpayWithdrawProperty.getWithdrawType() == null || proxpayWithdrawProperty.getWithdrawType().equals(1)) {
            FinanceWithdrawCwgUnioniDto dto = new FinanceWithdrawCwgUnioniDto();

            dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                    withdrawDto.getTransformAmount().setScale(0, BigDecimal.ROUND_DOWN));
            dto.setBankUname(withdrawDto.getBankUname());
            dto.setName(withdrawDto.getName());
            dto.setBankCardNum(withdrawDto.getBankCardNum());
            dto.setBankBranchName(withdrawDto.getBankBranchName());

            if (!Strings.isNullOrEmpty(withdrawDto.getBankName())) {
                String[] sp = withdrawDto.getBankName().split("-");
                dto.setBankName(sp[0]);
            }
            dto.setId(withdrawDto.getId());
            dto.setSerial(withdrawDto.getSerial());

            dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());
            dto.setPayType(entity.getPayType());
            return dto;
        } else {
            FinanceWithdrawCwgAliDto dto = new FinanceWithdrawCwgAliDto();


            dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                    withdrawDto.getTransformAmount().setScale(0, BigDecimal.ROUND_DOWN));
            dto.setName(withdrawDto.getName());
            dto.setWalletAddress(withdrawDto.getAddress());

            dto.setId(withdrawDto.getId());
            dto.setSerial(withdrawDto.getSerial());

            dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());
            dto.setPayType(entity.getPayType());
            return dto;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdraw(FinanceWithdrawCwgEntity entity) throws Exception {

        FinanceWithdrawApproveChannelSubmitEntity financeWithdrawApproveChannelSubmitEntity =
                new FinanceWithdrawApproveChannelSubmitEntity();
        financeWithdrawApproveChannelSubmitEntity.setSubmitStatus(PayConstants.PASSED_STATUS);
        financeWithdrawApproveChannelSubmitEntity.setId(entity.getId());
        financeWithdrawApproveChannelSubmitEntity.setActualPayType(entity.getPayType());
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);

        } else {
            //financeAgentWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);
        }

        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());

        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }

        entity.setCId(withdrawDto.getCId());
        entity.setSerial(withdrawDto.getSerial());
        entity.setLogin(withdrawDto.getLogin());

        CwgWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(entity.getPayType(),
                CwgWithdrawPropertyEntity.class);
        entity.setWithdrawType(proxpayWithdrawProperty.getWithdrawType());
        payout(entity, proxpayWithdrawProperty);

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdrawCallback(Integer withdrawType, Map<String, String> callbackEntity) throws Exception {
        log.info(String.format("withdrawCallback orderNo:%s,callback data:%s",
                callbackEntity.get("shopOrderNo"), JSON.toJSONString(callbackEntity)));

        String payType = null;
        if (withdrawType == null || withdrawType.equals(1)) {
            FinanceWithdrawTable table = financeWithdrawService.getBySerial(callbackEntity.get("shopOrderNo"));
            payType = table.getActualPayType();

        } else {
           /* FinanceAgentWithdrawTable table = financeAgentWithdrawService.getBySerial(callbackEntity.get("shopOrderNo"
            ));
            payType = table.getActualPayType();*/
        }
        CwgWithdrawPropertyEntity payProperty = getWithdrawProperty(payType,
                CwgWithdrawPropertyEntity.class);
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }
//        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
//                getFinanceDepositCallbackUpdateEntity(PayConstants.OFA_PAY_SUCCESS_STATUS,
//                        callbackEntity.getOrderid(),
//                        callbackEntity.getOrderno(),
//                        callbackEntity.getStatus(), callbackEntity);
//        //计算实际金额
//        BigDecimal amount = new BigDecimal(callbackEntity.getAmount());
//
//        balanceCallbackUpdateEntity.setTransformAmount(amount);

        FinanceWithdrawApproveChannelEntity financeWithdrawApproveChannelEntity =
                new FinanceWithdrawApproveChannelEntity();
        financeWithdrawApproveChannelEntity.setChannelSerial(callbackEntity.get("orderNo"));

        if (PayConstants.CWG_PAY_TYPE_REMIT_SUCCESS_STATUS.equals(callbackEntity.get("status"))) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_SUCCESS_STATUS);
        } else {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        }
        financeWithdrawApproveChannelEntity.setSerial(callbackEntity.get("shopOrderNo"));
        /**
         * 更新数据
         */

        if (withdrawType == null || withdrawType.equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelEntity);

        } else {
            //financeAgentWithdrawService.channel(financeWithdrawApproveChannelEntity);
        }

    }

    private void payout(FinanceWithdrawCwgEntity entity, CwgWithdrawPropertyEntity payProperty) throws ServiceException
            , IOException {
        String datetime = DateUtil.convertDateBetweenTimeZone("GMT+0", new Date());
        Map<String, String> params = new HashMap<>(20);
        params.put("shopOrderNo", entity.getSerial());
        params.put("shopId", String.valueOf(payProperty.getShopId()));
        params.put("amount", entity.getAmount().stripTrailingZeros().toPlainString());

        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            params.put("callbackUrl", payProperty.getCallbackUrl());

        } else {
            params.put("callbackUrl", payProperty.getAgentCallbackUrl());
        }
        params.put("name", entity.getName());
        params.put("requestTime", datetime);
        params.put("bankUname", entity.getBankUname());
        params.put("bankCardNum", entity.getBankCardNum());
        params.put("bankName", entity.getBankName());
        params.put("bankBranchName", entity.getBankBranchName());
        params.put("bankAddr", entity.getBankAddr());
        params.put("swiftCode", entity.getSwiftCode());
        params.put("bankCode", entity.getBankCode());
        params.put("walletAddress", entity.getWalletAddress());
        params.put("withdrawType", String.valueOf(entity.getWithdrawType()));


        String sign = getSign(payProperty, params);
        params.put("sign", sign);

        String payUrl = payProperty.getPayoutUrl();

        log.info(String.format("cwg payout:%s,data:%s", payUrl, JSON.toJSONString(params)));


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

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


        String payString = response.body();

        log.info("cwg payout response body:" + payString);

        JSONObject jsonObject = JSON.parseObject(payString);
        if (!jsonObject.getInteger("code").equals(200)) {
            throw ServiceException.exception(jsonObject.getString("msg"));
        }
    }


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

        String _sign = callbackEntity.get("sign");
        callbackEntity.remove("sign");
        String sign = getCallbackSign(payProperty.getSecretKey(), 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);
    }

    private boolean validate(Map<String, String> callbackEntity, CwgWithdrawPropertyEntity payProperty) throws Exception {

        String _sign = callbackEntity.get("sign");
        callbackEntity.remove("sign");
        String sign = getCallbackSign(payProperty.getSecretKey(), 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(CwgPayPropertyEntity propertyEntity, Map<String, String> params) {

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


        return sign;
    }

    private String getSign(CwgWithdrawPropertyEntity propertyEntity, Map<String, String> params) {

        String signString = MapUtil.getStringSortByKey(params);
        String sign = MD5Util.getMD5(signString + propertyEntity.getSecretKey());
        String order = params.get("shopOrderNo");
        if (Strings.isNullOrEmpty(order)) {
            log.info(String.format("cwg pay response--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 secretKey
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getCallbackSign(String secretKey, Map<String, String> callbackEntity) {

        String signString = MapUtil.getStringSortByKey(callbackEntity);
        String sign = MD5Util.getMD5(signString + secretKey);

        log.info(String.format("回调结果加密处理--请求orderNo:%s,sign string:%s,sign:%s", callbackEntity.get("shopOrderNo"),
                signString,
                sign));

        return sign;
    }
}
