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.FinanceWithdrawIbitDto;
import com.crm.pay.entity.callback.IbitCallbackEntity;
import com.crm.pay.entity.property.IbitPayPropertyEntity;
import com.crm.pay.entity.property.IbitPayWithdrawPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.IbitService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.pay.utils.RSAIbitUtil;
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.custom.info.CustomInfoEntity;
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.FinanceWithdrawIbitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawInfoEntity;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
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.HttpUtil;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Service
public class IbitServiceImpl extends BasePayServiceImpl implements IbitService {

    @Autowired
    private FinanceDepositService financeDepositService;

    /**
     * 获取支付form
     *
     * @param
     * @return
     * @throws ServiceException
     */
    private static String getUrl(IbitPayPropertyEntity propertyEntity, Map<String, String> map) throws Exception {


        String sign = getSign(propertyEntity.getPublicKey(), map);

        map.put("encryptedData", sign);
//        String formString = PayFormUtil.getForm(propertyEntity.getBaseUrl() + propertyEntity.getDepositUrl(), map);

//        log.info(String.format("支付请求,请求orderNo:%s,请求form:%s", financeDepositTable.getSerial(), formString));

        //
        String bodyJsonString = JSON.toJSONString(map);
        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() + propertyEntity.getDepositUrl(),
                JSON.toJSONString(bodyJsonString)));
        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getDepositUrl()
                , header,
                JSON.toJSON(map));
        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 || jsonObject.getInteger("code") >= 40000) {
            throw new PayValidatedException(jsonObject.getString("message"));
        }

        String url = jsonObject.getJSONObject("data").getString("url");
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("merchantOrderNo"), url));
        return url;
    }

    public static void main(String[] args) throws Exception {
//        IbitPayPropertyEntity propertyEntity = new IbitPayPropertyEntity();
//        propertyEntity.setMerchantNo("880032");
//        propertyEntity.setPublicKey("MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKZuuowQrwKFvmNNLSkWMS739e9F6C5lXekQ
//        +dxZABhoMLJJNZkQJy526SuCbwXHeuV0ib1D2rqYP36oxKxg1XMCAwEAAQ==");
//
//        propertyEntity.setRedirectUrl("");
//        propertyEntity.setBaseUrl("http://openapi-dev.ibitpay.io");
//        propertyEntity.setDepositUrl("/api/open/order/createBuyOrder");
//        FinanceDepositTable table = new FinanceDepositTable();
//        table.setSerial("DB145616891561156");
//        table.setName("zhangsan");
//        table.setLogin(700112L);
//        table.setTransformAmount(new BigDecimal("100.32"));
//        table.setTransformCurrency("CNY");
//        String formString = getForm(propertyEntity, table);
//        System.out.println(formString);

    }

    private static String getSign(String publicKey, Map<String, String> map) throws Exception {

        String jsonString = JSON.toJSONString(map);
        String signString = RSAIbitUtil.encryptByPublicKey(jsonString, publicKey);

        log.info(String.format("orderNo:%s,sign string:%s,sign:%s", map.get("merchantOrderNo"), jsonString,
                signString));

        return signString;
    }

    /**
     * 通道支付业务逻辑 包括加密 汇率等
     *
     * @param balanceSaveEntity 支付实体
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity, CustomInfoEntity infoEntity) throws Exception {
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());

        IbitPayPropertyEntity payProperty = getPayProperty(channel, IbitPayPropertyEntity.class);


        balanceSaveEntity.setCurrency(channel.getCurrency());

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

        Map<String, String> map = new HashMap<>(6);
        map.put("merchantNo", payProperty.getMerchantNo());
        map.put("amount", String.format("%.2f", financeDepositTable.getTransformAmount().setScale(0,
                BigDecimal.ROUND_UP).doubleValue()));
        map.put("currencyType", financeDepositTable.getTransformCurrency());
        map.put("merchantOrderNo", financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin());
        map.put("customerName", financeDepositTable.getName());
//        map.put("customerCountry", financeDepositTable.getCountry());
//        map.put("customerIdCardNumber", infoEntity.getIdentity());
//        map.put("customerPhoneNumber", infoEntity.getPhone());
        map.put("redirectUrl", payProperty.getRedirectUrl());
        map.put("hideUsdt", "true");

        //获取form表单数据
        String url = getUrl(payProperty, map);

        //将form字符串加入到实体
        financeDepositTable.setFormUrl(url);
        
        return url;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceMerchantDepositAddEntity balanceSaveEntity) throws Exception {
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());

        IbitPayPropertyEntity payProperty = getPayProperty(channel, IbitPayPropertyEntity.class);


        balanceSaveEntity.setCurrency(channel.getCurrency());

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

        Map<String, String> map = new HashMap<>(6);
        map.put("merchantNo", payProperty.getMerchantNo());
        map.put("amount", String.format("%.2f", financeDepositTable.getTransformAmount().setScale(0,
                BigDecimal.ROUND_UP).doubleValue()));
        map.put("currencyType", financeDepositTable.getTransformCurrency());
        map.put("merchantOrderNo", financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin());
        map.put("customerName", balanceSaveEntity.getName());
        map.put("customerCountry", financeDepositTable.getCountry());
        map.put("customerIdCardNumber", balanceSaveEntity.getPhone());
        map.put("customerPhoneNumber", balanceSaveEntity.getIdentity());
        map.put("redirectUrl", payProperty.getRedirectUrl());
        map.put("hideUsdt", "true");

        //获取form表单数据
        String url = getUrl(payProperty, map);

        //将form字符串加入到实体
        financeDepositTable.setFormUrl(url);
        
        return url;
    }

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

        callback(callbackEntity, PayConstants.IBIT_PAY_TYPE);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(IbitCallbackEntity callbackEntity, String payType) throws Exception {
        log.info(String.format("Ibit callback  data:%s", JSON.toJSONString(callbackEntity)));
        if (Strings.isNullOrEmpty(callbackEntity.getEncryptedData())) {
            log.error("Ibit pay callback data is null");
            throw new ServiceException("fail");
        }
        IbitPayPropertyEntity payProperty = getPayProperty(payType, false,
                IbitPayPropertyEntity.class);

        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw new ServiceException("fail");
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getMerchantOrderNo(), callbackEntity.getOrderNo(),
                        PayConstants.CALLBACE_SUCCESS_STATUS, callbackEntity);
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
    }

    @Override
    public FinanceWithdrawIbitDto 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());
        }
        entity.setPayType(PayConstants.IBIT_PAY_TYPE_REMIT);

        SysRemitChannelTable withdrawChannel = getWithdrawChannel(entity.getPayType());

        FinanceWithdrawIbitDto dto = new FinanceWithdrawIbitDto();
        dto.setCurrencyType(withdrawDto.getTransformCurrency());
        dto.setBankAccountName(withdrawDto.getName());
        dto.setBankAccountNumber(withdrawDto.getBankCardNum());
        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                String.valueOf(withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN)));
        dto.setBankName(withdrawDto.getBankName());
        dto.setBankBranchName(withdrawDto.getBankBranchName());

        dto.setId(withdrawDto.getId());
        dto.setSerial(withdrawDto.getSerial());
        dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());
        dto.setPayType(entity.getPayType());
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void withdraw(FinanceWithdrawIbitEntity 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());
        }
        entity.setCId(withdrawDto.getCId());
        entity.setSerial(withdrawDto.getSerial());
        entity.setLogin(withdrawDto.getLogin());

        IbitPayWithdrawPropertyEntity ibitPayWithdrawProperty = getWithdrawProperty(PayConstants.IBIT_PAY_TYPE_REMIT,
                IbitPayWithdrawPropertyEntity.class);

        payout(entity, ibitPayWithdrawProperty);

        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);
        }
    }

    private void payout(FinanceWithdrawIbitEntity entity, IbitPayWithdrawPropertyEntity propertyEntity) throws Exception {
        log.info(String.format("payout json:%s", JSON.toJSON(entity)));

        Map<String, String> map = new HashMap<>();
        map.put("merchantNo", propertyEntity.getMerchantNo());
        map.put("merchantOrderNo", entity.getSerial());
        map.put("currencyType", entity.getCurrencyType());
        map.put("bankAccountName", entity.getBankAccountName());
        map.put("bankAccountNumber", entity.getBankAccountNumber());
        map.put("amount", entity.getAmount());
        map.put("bankName", entity.getBankName());
        map.put("bankBranchName", entity.getBankBranchName());

        String sign = getSign(propertyEntity.getPublicKey(), map);
        map.put("encryptedData", sign);

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

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

        if (response.statusCode() != 200) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");

        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdrawCallback(Integer withdrawType, IbitCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback data:%s", callbackEntity));

        IbitPayWithdrawPropertyEntity withdrawProperty = getWithdrawProperty(PayConstants.IBIT_PAY_TYPE_REMIT,
                IbitPayWithdrawPropertyEntity.class);
        // 验证签名
        boolean flag = withdrawCallbackValidate(callbackEntity, withdrawProperty);
        if (!flag) {
            log.error("ibit withdraw callback verification signature error");
            return Constants.FAIL;
        }
        Integer status = callbackEntity.getState();
        String merchantOrderNo = callbackEntity.getMerchantOrderNo();
        String orderNo = callbackEntity.getOrderNo();


        log.info("ibit withdraw callback--Status:" + status + ", merchantOrderNo:" + merchantOrderNo + ", orderNo:" + orderNo);

        FinanceWithdrawApproveChannelEntity financeWithdrawApproveChannelEntity =
                new FinanceWithdrawApproveChannelEntity();
        financeWithdrawApproveChannelEntity.setChannelSerial(orderNo);

        // 状态 Success Approved Failed Rejected
        if (status == null || status != 4) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        } else {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_SUCCESS_STATUS);
        }
        financeWithdrawApproveChannelEntity.setSerial(merchantOrderNo);

        /**
         * 更新数据
         */
        if (withdrawType == null || withdrawType.equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelEntity);
        } else {
            //financeAgentWithdrawService.channel(financeWithdrawApproveChannelEntity);
        }

        return Constants.SUCCESS;

    }

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

        try {
            String content = RSAIbitUtil.decryptByPrivateKey(callbackEntity.getEncryptedData(),
                    payProperty.getPrivateKey());
            IbitCallbackEntity entity = JSON.parseObject(content, IbitCallbackEntity.class);
            entity.setEncryptedData(callbackEntity.getEncryptedData());
            BeanUtils.copyProperties(entity, callbackEntity);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 验证出金回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean withdrawCallbackValidate(IbitCallbackEntity callbackEntity,
                                             IbitPayWithdrawPropertyEntity payProperty) throws Exception {
        try {
            String content = RSAIbitUtil.decryptByPrivateKey(callbackEntity.getEncryptedData(),
                    payProperty.getPrivateKey());
            IbitCallbackEntity entity = JSON.parseObject(content, IbitCallbackEntity.class);
            entity.setEncryptedData(callbackEntity.getEncryptedData());
            BeanUtils.copyProperties(entity, callbackEntity);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}
