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

import com.alibaba.fastjson.JSON;
import com.crm.pay.dto.callback.ecom.EcomResultDto;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawEcomDto;
import com.crm.pay.entity.callback.EcomCallbackEntity;
import com.crm.pay.entity.ecom.EcomCashCreateOrderEntity;
import com.crm.pay.entity.ecom.EcomCreateOrderEntity;
import com.crm.pay.entity.ecom.EcomPaymentresponseEntity;
import com.crm.pay.entity.ecom.EcomPayoutResponseEntity;
import com.crm.pay.entity.property.EcomPropertyEntity;
import com.crm.pay.entity.property.EcomWithdrawPropertyEntity;
import com.crm.pay.service.EcomService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.FinanceWithdrawService;
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.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawEcomEntity;
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.AESUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MD5Util;
import com.crm.rely.backend.util.SignUtil;
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.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: houn
 * @create: 2024-01-20 12:45
 */
@Slf4j
@Service
public class EcomServiceImpl extends BasePayServiceImpl implements EcomService {

    @Autowired
    private FinanceDepositService financeDepositService;


    @Autowired
    private FinanceWithdrawService financeWithdrawService;
    /*@Autowired
    private FinanceAgentWithdrawService financeAgentWithdrawService;*/

    public static void main(String[] args) throws Exception {

        System.out.println(AESUtil.encrypt("", "bfa5559109f94c78af615bcf00d52060"));

        System.out.println(AESUtil.decrypt("",
                "bfa5559109f94c78af615bcf00d52060"));
    }

    /**
     * @param balanceSaveEntity 支付实体
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        SysRemittanceChannelTable channel = getPayChannel(balanceSaveEntity.getPayType());

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

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

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

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

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

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public EcomResultDto callback(EcomCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.getTranscode(),
                JSON.toJSONString(callbackEntity)));

        EcomResultDto resultDto = new EcomResultDto();
        resultDto.setTransCode(callbackEntity.getTranscode());
        boolean flag = validate(callbackEntity);
        if (!flag) {
            resultDto.setResponseCode(500);
            return resultDto;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.ECOM_PAY_SUCCESS_STATUS,
                        callbackEntity.getTranscode(),
                        callbackEntity.getOrderTranscode(),
                        callbackEntity.getResponseCode(),
                        callbackEntity);
        if (PayConstants.CALLBACE_SUCCESS_STATUS.equals(balanceCallbackUpdateEntity.getCallbackStatus()) && callbackEntity.getPaidAmount() != null) {
            balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getPaidAmount()));

        } else {
            balanceCallbackUpdateEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        }
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);

        resultDto.setResponseCode(1);
        return resultDto;
    }

    /**
     * 获取支付form
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(EcomPropertyEntity propertyEntity, FinanceDepositTable balanceTable) throws ServiceException,
            IOException, PayValidatedException {
        Map<String, String> header = new HashMap<>(2);
        header.put("Content-Type", "application/json");


        EcomCreateOrderEntity orderEntity = new EcomCreateOrderEntity();
        orderEntity.setTranscode(balanceTable.getSerial() + "_" + balanceTable.getLogin());
        orderEntity.setPartnerCode(propertyEntity.getPartnerCode());
        orderEntity.setServiceCode(propertyEntity.getServiceCode());
        orderEntity.setOrderDescription(String.valueOf(balanceTable.getLogin()));
//        orderEntity.setBuyerFullname(balanceTable.getName());
        orderEntity.setBuyerFullname(propertyEntity.getBuyerFullname());
        orderEntity.setAmount(balanceTable.getTransformAmount().setScale(0, RoundingMode.HALF_UP).intValue());
        orderEntity.setBankCode(balanceTable.getBankCode());
        orderEntity.setNotifyUrl(propertyEntity.getNoticeUrl());

        String sign = getPaySign(propertyEntity, orderEntity);

        orderEntity.setRequestSignature(sign);

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

        Connection.Response response = HttpUtil.post(
                payUrl, header, orderEntity);

        log.info(String.format("get ecom data, url:%s,header:%s,params:%s,body:%s",
                payUrl, JSON.toJSONString(header), JSON.toJSONString(orderEntity), response.body()));

        EcomPaymentresponseEntity ecomPaymentresponseEntity = JSON.parseObject(response.body(),
                EcomPaymentresponseEntity.class);


        if (ecomPaymentresponseEntity == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        if (ecomPaymentresponseEntity.getResponseCode() != 1) {
            if (Strings.isNullOrEmpty(ecomPaymentresponseEntity.getResponseMessage())) {
                throw new PayValidatedException("Payment platform is abnormal, please try again later or contact " +
                        "customer" +
                        " service.");
            } else {
                throw new PayValidatedException(ecomPaymentresponseEntity.getResponseMessage());
            }
        }
        String url = ecomPaymentresponseEntity.getViertQr();

        log.info(String.format("ecom payment,orderNo:%s,url:%s", orderEntity.getTranscode(), url));

        return url;
    }

    @Override
    public FinanceWithdrawEcomDto 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.PAY_ECOM_REMIT_KEY);

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

        FinanceWithdrawEcomDto dto = new FinanceWithdrawEcomDto();
        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setBankAccountName(withdrawDto.getName());
        dto.setBankAccountNo(withdrawDto.getBankCardNum());
        dto.setBankCode(withdrawDto.getBankCode());

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

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdraw(FinanceWithdrawEcomEntity 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());

        EcomWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(PayConstants.PAY_ECOM_REMIT_KEY,
                EcomWithdrawPropertyEntity.class);

        payout(entity, proxpayWithdrawProperty);

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

        return null;
    }

    private void payout(FinanceWithdrawEcomEntity entity, EcomWithdrawPropertyEntity propertyEntity) throws ServiceException
            , IOException, PayValidatedException {

        EcomCashCreateOrderEntity orderEntity = new EcomCashCreateOrderEntity();
        orderEntity.setTranscode(entity.getSerial());
        orderEntity.setPartnerCode(propertyEntity.getPartnerCode());
        orderEntity.setServiceCode(propertyEntity.getServiceCode());
        orderEntity.setAmount(entity.getAmount().setScale(0, RoundingMode.HALF_DOWN).intValue());
        orderEntity.setBankCode(entity.getBankCode());
        orderEntity.setBankAccountNo(entity.getBankAccountNo());
        orderEntity.setBankAccountName(entity.getBankAccountName());
        orderEntity.setBankTransferContent(entity.getBankTransferContent());
        orderEntity.setExtraInfo(entity.getExtraInfo());

        String sign = getPaySign(propertyEntity, orderEntity);

        orderEntity.setRequestSignature(sign);

        String payCashUrl = propertyEntity.getBaseUrl() + propertyEntity.getCreateOrderUrl();

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

        Connection.Response response = HttpUtil.post(
                payCashUrl, header, orderEntity);

        log.info(String.format("get ecom data, url:%s,header:%s,params:%s,body:%s",
                payCashUrl, JSON.toJSONString(header), JSON.toJSONString(orderEntity), response.body()));

        EcomPayoutResponseEntity responseEntity = JSON.parseObject(response.body(),
                EcomPayoutResponseEntity.class);


        if (responseEntity == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        if (responseEntity.getResponseCode() != 1) {
            if (Strings.isNullOrEmpty(responseEntity.getResponseMessage())) {
                throw new PayValidatedException("Payment platform is abnormal, please try again later or contact " +
                        "customer" +
                        " service.");
            } else {
                throw new PayValidatedException(responseEntity.getResponseMessage());
            }
        }
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(EcomCallbackEntity callbackEntity) throws Exception {
        EcomPropertyEntity payProperty = getPayProperty(PayConstants.ECOM_PAY_TYPE_KEY, EcomPropertyEntity.class);
        String signString = String.format("%s%s%s", callbackEntity.getTranscode(), payProperty.getPartnerCode(),
                payProperty.getPartnerApiKey());

        return SignUtil.verifySignSHA256withRSA(payProperty.getPublicKey(), signString,
                callbackEntity.getResponseSignature());
    }

    private String getPaySign(EcomPropertyEntity propertyEntity, EcomCreateOrderEntity orderEntity) throws UnsupportedEncodingException {


        String signString = String.format("%s%s%s", orderEntity.getTranscode(), propertyEntity.getPartnerCode(),
                propertyEntity.getPartnerApiKey());

        String sign;
        try {
            sign = MD5Util.getMD5(signString);
        } catch (Exception e) {
            sign = null;
        }

        log.info(String.format("ecom--orderNo:%s,sign string:%s,sign:%s", orderEntity.getTranscode(), signString,
                sign));

        return sign;
    }

    private String getPaySign(EcomWithdrawPropertyEntity propertyEntity, EcomCashCreateOrderEntity orderEntity) throws UnsupportedEncodingException {


        String signString = String.format("%s%s%s", orderEntity.getTranscode(), propertyEntity.getPartnerCode(),
                propertyEntity.getPartnerApiKey());

        String sign;
        try {
            sign = MD5Util.getMD5(signString);
        } catch (Exception e) {
            sign = null;
        }

        log.info(String.format("ecom--orderNo:%s,sign string:%s,sign:%s", orderEntity.getTranscode(), signString,
                sign));

        return sign;
    }
}
