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.FinanceWithdrawCheezeepayDto;
import com.crm.pay.entity.property.CheezeepayPropertyEntity;
import com.crm.pay.entity.property.CheezeepayWithdrawPropertyEntity;
import com.crm.pay.service.CheezeepayService;
import com.crm.pay.service.CustomInfoService;
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.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawCheezeepayEntity;
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.*;
import com.crm.rely.backend.util.AESUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.MapUtil;
import com.crm.rely.backend.util.SignUtil;
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.*;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2024-10-13 11:45
 */
@Slf4j
@Service
public class CheezeepayServiceImpl extends BasePayServiceImpl implements CheezeepayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private CustomInfoService customInfoService;

    public static void main(String[] args) {
        System.out.println(AESUtil.encrypt("123123", "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.decrypt("eWNjOnzrfFrP33giazVchQ==",
                "bfa5559109f94c78af615bcf00d52060"));

    }

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

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


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

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

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

        log.info(String.format("callback orderNo:%s,callback data:%s", callbackEntity.get("mchOrderNo"),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        CheezeepayPropertyEntity payProperty = getPayProperty(payType, false,
                CheezeepayPropertyEntity.class);

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

        List<String> status = new ArrayList<>(2);
        status.add(PayConstants.CHEEZEEPAY_PAY_TYPE_SUCCESS_STATUS);
        status.add(PayConstants.CHEEZEEPAY_PAY_TYPE_PARTIAL_SUCCESS_STATUS);

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(status,
                        callbackEntity.get("mchOrderNo"),
                        callbackEntity.get("platOrderNo"),
                        callbackEntity.get("orderStatus"), callbackEntity);

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

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

    }

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

        CustomInfoTable customInfoTable = customInfoService.getById(balanceTable.getCustomId());

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, String> params = new HashMap<>(10);
        params.put("appId", propertyEntity.getAppId());
        params.put("merchantId", propertyEntity.getMerchantId());
        params.put("mchOrderNo", orderNo);
        params.put("paymentMode", propertyEntity.getPaymentMode());

        params.put("amount",
                balanceTable.getTransformAmount().setScale(2, BigDecimal.ROUND_UP).stripTrailingZeros().toPlainString());

//        params.put("amount", balanceTable.getAmount().stripTrailingZeros().toPlainString());
        if (balanceSaveEntity.getPayType().equals(PayConstants.CHEEZEEPAY_PAY_TYPE)) {
            params.put("name", balanceTable.getName());
            params.put("phone", customInfoTable.getAreaCode() + customInfoTable.getPhone());
            params.put("email", customInfoTable.getEmail());
            params.put("language", "en");
            params.put("returnUrl", propertyEntity.getPickupUrl());
        } else if (balanceSaveEntity.getPayType().equals(PayConstants.CHEEZEEPAY_THAILAND_PAY_TYPE)) {
        } else if (balanceSaveEntity.getPayType().equals(PayConstants.CHEEZEEPAY_BRAZIL_PAY_TYPE)) {
            params.put("name", balanceTable.getName());
            params.put("language", "pt_br");
        } else if (balanceSaveEntity.getPayType().equals(PayConstants.CHEEZEEPAY_INDONESIA_PAY_TYPE)) {
            params.put("name", balanceTable.getName());
            params.put("phone", customInfoTable.getAreaCode() + customInfoTable.getPhone());
            params.put("email", customInfoTable.getEmail());
            params.put("productDetail", customInfoTable.getEmail());
            params.put("redirectUrl", propertyEntity.getPickupUrl());
            params.put("language", "en");
            params.put("amount",
                    balanceTable.getTransformAmount().setScale(0, BigDecimal.ROUND_UP).stripTrailingZeros().toPlainString());
            params.put("paymentMode", balanceSaveEntity.getBankCode());
        }

        params.put("notifyUrl", propertyEntity.getCallbackUrl());
        params.put("timestamp", String.valueOf(new Date().getTime()));

        String sign = getSign(propertyEntity, params);

        params.put("sign", sign);
        Map<String, String> headers = new HashMap<>(2);
        headers.put("Content-Type", "application/json");
        headers.put("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0" +
                ".4577.63 Safari/537.36");


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

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

        log.info(String.format("url:%s,json:%s,body:%s", payUrl,
                JSON.toJSON(params), response.body()));
        JSONObject requestEntity = JSON.parseObject(response.body());

        if (requestEntity == null
                || (requestEntity.get("code") != null && !requestEntity.getString("code").equals("000000"))) {
            throw new PayValidatedException(requestEntity.getString("msg"));
        }

        String url = String.valueOf(requestEntity.get("cashierLink"));
        log.info(String.format("orderNo:%s,url:%s", params.get("mchOrderNo"), url));
        return url;
    }


    @Override
    public FinanceWithdrawCheezeepayDto 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());
        CheezeepayWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawPayProperty(withdrawChannel,
                CheezeepayWithdrawPropertyEntity.class);

        FinanceWithdrawCheezeepayDto dto = new FinanceWithdrawCheezeepayDto();

        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setName(withdrawDto.getName());
        dto.setAccountNumber(withdrawDto.getBankCardNum());
        dto.setIfscCode(withdrawDto.getCustomBankCode());
        dto.setBankName(withdrawDto.getBankName());
        dto.setBranchName(withdrawDto.getBankBranchName());

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

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

        dto.setPaymentMethod(proxpayWithdrawProperty.getPaymentMode());
        return dto;
    }

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

        CheezeepayWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(entity.getPayType(),
                CheezeepayWithdrawPropertyEntity.class);
        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("mchOrderNo"), JSON.toJSONString(callbackEntity)));

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

        } else {
            /*FinanceAgentWithdrawTable table = financeAgentWithdrawService.getBySerial(callbackEntity.get("mchOrderNo"
            ));
            payType = table.getActualPayType();*/
        }
        CheezeepayWithdrawPropertyEntity payProperty = getWithdrawProperty(payType,
                CheezeepayWithdrawPropertyEntity.class);
        boolean flag = validate(callbackEntity, payProperty);
        if (!flag) {
            throw new ServiceException(Constants.FAIL);
        }

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

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

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

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

    }

    private void payout(FinanceWithdrawCheezeepayEntity entity, CheezeepayWithdrawPropertyEntity payProperty) throws ServiceException
            , IOException {

        CustomInfoTable customInfoTable = customInfoService.getByCId(entity.getCId());
        Map<String, String> params = new HashMap<>(20);
        params.put("appId", payProperty.getAppId());
        params.put("merchantId", payProperty.getMerchantId());
        params.put("mchOrderNo", entity.getSerial());
        params.put("paymentMethod", entity.getPaymentMethod());
        params.put("amount", entity.getAmount().stripTrailingZeros().toPlainString());
        params.put("name", entity.getName());
        params.put("phone", customInfoTable.getAreaCode() + customInfoTable.getPhone());
        params.put("email", customInfoTable.getEmail());
        params.put("language", "en");
        params.put("timestamp", String.valueOf(new Date().getTime()));

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

        } else {
            params.put("notifyUrl", payProperty.getAgentCallbackUrl());
        }

        Map<String, String> payeeAccountInfos = new HashMap<>(20);
        payeeAccountInfos.put("name", entity.getName());
        payeeAccountInfos.put("accountNumber", entity.getAccountNumber());
        payeeAccountInfos.put("ifscCode", entity.getIfscCode());
        payeeAccountInfos.put("accountType", payProperty.getAppId());
        payeeAccountInfos.put("bankName", entity.getBankName());
        payeeAccountInfos.put("branchName", entity.getBranchName());

        params.put("payeeAccountInfos", JSON.toJSONString(payeeAccountInfos));


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

        String payUrl = payProperty.getBaseUrl() + payProperty.getPayoutUrl();

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


        Map<String, String> headers = new HashMap<>(2);
        headers.put("Content-Type", "application/json");
        headers.put("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0" +
                ".4577.63 Safari/537.36");

        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.getString("code").equals("000000")) {
            throw ServiceException.exception(jsonObject.getString("msg"));
        }
    }


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

        String _sign = callbackEntity.get("sign");
        callbackEntity.remove("sign");
        String signString = MapUtil.getStringSortByKey(callbackEntity);
        return SignUtil.verifySignSHA256withRSA(payProperty.getPublicKey(), signString, _sign);

    }

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

        String _sign = callbackEntity.get("sign");
        callbackEntity.remove("sign");
        String signString = MapUtil.getStringSortByKey(callbackEntity);
        return SignUtil.verifySignSHA256withRSA(payProperty.getPublicKey(), signString, _sign);

    }

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

        String signString = MapUtil.getStringSortByKey(params);
        String sign = SignUtil.signSHA256withRSA(propertyEntity.getPrivateKey(), signString);
        String order = params.get("mchOrderNo");
        log.info(String.format("orderNo:%s,sign string:%s,sign:%s", order, signString, sign));


        return sign;
    }

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

        String signString = MapUtil.getStringSortByKey(params);
        String sign = SignUtil.signSHA256withRSA(propertyEntity.getPrivateKey(), signString);
        String order = params.get("mchOrderNo");
        log.info(String.format("orderNo:%s,sign string:%s,sign:%s", order, signString, sign));


        return sign;
    }

}
