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.FinanceWithdrawOfaDto;
import com.crm.pay.entity.callback.ofa.OfaPayCallbackEntity;
import com.crm.pay.entity.callback.ofa.OfaPayoutCallbackEntity;
import com.crm.pay.entity.property.OfaPayPropertyEntity;
import com.crm.pay.entity.property.OfaWithdrawPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.OfaPayService;
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.FinanceWithdrawInfoEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawOfaEntity;
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.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.MapUtil;
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.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: houn
 * @create: 2024-10-10 23:45
 */
@Slf4j
@Service
public class OfaPayServiceImpl extends BasePayServiceImpl implements OfaPayService {

    @Autowired
    private FinanceDepositService balanceService;

    public static void main(String[] args) {

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


    }

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

        //获取通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.OFA_PAY_TYPE);
        OfaPayPropertyEntity payProperty = getPayProperty(channel, OfaPayPropertyEntity.class);


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

        //获取支付地址
        String url = getUrl(balanceTable, payProperty);
        balanceTable.setFormUrl(url);
        
        return url;
    }

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


        boolean flag = validate(callbackEntity);
        if (!flag || !queryValidateOrderInfo(callbackEntity)) {
            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);
        /**
         * 更新数据
         */
        balanceService.updateCallback(balanceCallbackUpdateEntity);
    }


    @Override
    public FinanceWithdrawOfaDto 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.OFA_PAY_TYPE_REMIT);
        SysRemitChannelTable withdrawChannel = getWithdrawChannel(entity.getPayType());
        OfaWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawPayProperty(withdrawChannel,
                OfaWithdrawPropertyEntity.class);

        FinanceWithdrawOfaDto dto = new FinanceWithdrawOfaDto();
        dto.setMoney(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setAccountname(withdrawDto.getName());
        dto.setAccountno(withdrawDto.getBankCardNum());

        if (!Strings.isNullOrEmpty(withdrawDto.getBankName())) {
            String[] sp = withdrawDto.getBankName().split("-");
            if (sp.length >= 2) {
                dto.setBankname(sp[0]);
                dto.setBankno(sp[1]);
            }
        }
        dto.setId(withdrawDto.getId());
        dto.setSerial(withdrawDto.getSerial());

        dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());

//        dto.setPs(proxpayWithdrawProperty.getPs());
        setFinanceWithdrawOfaDto(dto, proxpayWithdrawProperty);
        dto.setPayType(entity.getPayType());
        return dto;
    }

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

        OfaWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(PayConstants.OFA_PAY_TYPE_REMIT,
                OfaWithdrawPropertyEntity.class);

        payout(entity, proxpayWithdrawProperty);

        return null;
    }

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


        boolean flag = validate(callbackEntity);
        if (!flag || !queryValidateWithdrawOrderInfo(callbackEntity)) {
            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.getOrderno());

        if (PayConstants.OFA_PAY_TYPE_REMIT_SUCCESS_STATUS.equals(callbackEntity.getStatus())) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_SUCCESS_STATUS);
        } else {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        }
        financeWithdrawApproveChannelEntity.setSerial(callbackEntity.getOrderid());
        /**
         * 更新数据
         */

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

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

    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(FinanceDepositTable balanceTable, OfaPayPropertyEntity payProperty) throws ServiceException
            , IOException {

        Map<String, String> params = new HashMap<>(6);
        params.put("scode", payProperty.getScode());
        params.put("orderid", balanceTable.getSerial() + "_" + balanceTable.getLogin());
        params.put("paytype", payProperty.getPaytype());
        params.put("amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        params.put("productname", String.valueOf(balanceTable.getLogin()));
        params.put("currency", balanceTable.getTransformCurrency());
        params.put("userid", String.valueOf(balanceTable.getCId()));
        params.put("redirectpage", "0");
        params.put("noticeurl", payProperty.getNotifyUrl());
        params.put("callbackurl", payProperty.getReturnUrl());
        params.put("accountname", balanceTable.getName());

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

        String payUrl = payProperty.getPayUrl();
//        String formString = PayFormUtil.getForm(payProperty.getPayUrl(), params);


//        return formString;
//
        log.info(String.format("ofa pay create:%s,data:%s", payUrl, JSON.toJSONString(params)));


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

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


        String payString = response.body();

        log.info("ofa pay response body:" + payString);

        JSONObject ofaDataEntity = JSON.parseObject(payString);
        if (!ofaDataEntity.getString("status").equals("1") || !ofaDataEntity.getString("respcode").equals("00")) {
            throw ServiceException.exception(ofaDataEntity.getString("respmsg"));
        }

        return ofaDataEntity.getString("url");
//        return payString;
    }

    private void payout(FinanceWithdrawOfaEntity entity, OfaWithdrawPropertyEntity payProperty) throws ServiceException
            , IOException {

        Map<String, String> params = new HashMap<>(6);
        params.put("scode", payProperty.getScode());
        params.put("orderid", entity.getSerial());
        params.put("amount", String.format("%.2f", entity.getMoney().doubleValue()));
        params.put("bankname", entity.getBankname());
        params.put("accountno", entity.getAccountno());
        params.put("accountname", entity.getAccountname());
        params.put("bankno", entity.getBankno());
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            params.put("notifyurl", payProperty.getNotifyUrl());

        } else {
            params.put("notifyurl", payProperty.getAgentNotifyUrl());
        }

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

        String payUrl = payProperty.getPayoutUrl();

        log.info(String.format("ofa 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(headers, payUrl, params);


        String payString = response.body();

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

        JSONObject ofaDataEntity = JSON.parseObject(payString);
        if (!ofaDataEntity.getString("errcode").equals("00")) {
            throw ServiceException.exception(ofaDataEntity.getString("msg"));
        }
    }

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

        Map<String, String> params = new HashMap<>(15);
        params.put("scode", callbackEntity.getScode());
        params.put("orderid", callbackEntity.getOrderid());
        params.put("orderno", callbackEntity.getOrderno());
        params.put("paytype", callbackEntity.getPaytype());
        params.put("amount", callbackEntity.getAmount());
        params.put("productname", callbackEntity.getProductname());
        params.put("currency", callbackEntity.getCurrency());
        params.put("memo", callbackEntity.getMemo());
        params.put("resptime", callbackEntity.getResptime());
        params.put("status", callbackEntity.getStatus());
        params.put("respcode", callbackEntity.getRespcode());
        String sign = getSign(params);

        log.info(String.format("order no:%s callback result sign compare,md5--callback sign:%s,sign:%s",
                callbackEntity.getOrderid(),
                callbackEntity.getSign(), sign));
        return sign.equals(callbackEntity.getSign());

    }

    private boolean validate(OfaPayoutCallbackEntity callbackEntity) throws Exception {

        Map<String, String> params = new HashMap<>(15);
        params.put("scode", callbackEntity.getScode());
        params.put("orderid", callbackEntity.getOrderid());
        params.put("orderno", callbackEntity.getOrderno());
        params.put("money", callbackEntity.getMoney());
        params.put("status", callbackEntity.getStatus());
        params.put("resptime", callbackEntity.getResptime());
        params.put("respcode", callbackEntity.getRespcode());
        String sign = getSign(params);

        log.info(String.format("order no:%s callback result sign compare,md5--callback sign:%s,sign:%s",
                callbackEntity.getOrderid(),
                callbackEntity.getSign(), sign));
        return sign.equals(callbackEntity.getSign());

    }

    private boolean queryValidateOrderInfo(OfaPayCallbackEntity callbackEntity) throws Exception {
        OfaPayPropertyEntity payProperty = getPayProperty(PayConstants.OFA_PAY_TYPE,
                OfaPayPropertyEntity.class);

        Map<String, String> params = new HashMap<>(6);
        params.put("scode", payProperty.getScode());
        params.put("orderid", callbackEntity.getOrderid());

        String sign = getSign(params, payProperty);

        params.put("sign", sign);

        String queryUrl = payProperty.getQueryUrl();

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


        log.info(String.format("ofa get order info url:%s,headers:%s,data:%s", queryUrl, JSON.toJSONString(headers),
                JSON.toJSONString(params)));
        Connection.Response response = HttpUtil.post(queryUrl, headers, params);

        String payString = response.body();

        log.info(String.format("ofa get order info:%s", payString));

        JSONObject ofaOrderInfoDataEntity = JSON.parseObject(payString);
        if (!ofaOrderInfoDataEntity.getString("status").equals(PayConstants.OFA_PAY_SUCCESS_STATUS)) {
            return false;
        }
        return true;
    }

    private boolean queryValidateWithdrawOrderInfo(OfaPayoutCallbackEntity callbackEntity) throws Exception {
        OfaWithdrawPropertyEntity payProperty = getPayProperty(PayConstants.OFA_PAY_TYPE_REMIT,
                OfaWithdrawPropertyEntity.class);

        Map<String, String> params = new HashMap<>(6);
        params.put("scode", payProperty.getScode());
        params.put("orderid", callbackEntity.getOrderid());

        String sign = getSign(params, payProperty);

        params.put("sign", sign);

        String queryUrl = payProperty.getQueryUrl();

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


        log.info(String.format("ofa get order info url:%s,headers:%s,data:%s", queryUrl, JSON.toJSONString(headers),
                JSON.toJSONString(params)));
        Connection.Response response = HttpUtil.post(queryUrl, headers, params);

        String payString = response.body();

        log.info(String.format("ofa get order info:%s", payString));

        JSONObject ofaOrderInfoDataEntity = JSON.parseObject(payString);
        if (!ofaOrderInfoDataEntity.getString("status").equals(PayConstants.OFA_PAY_TYPE_REMIT_SUCCESS_STATUS)) {
            return false;
        }
        return true;
    }

    private String getSign(Map<String, String> params) throws Exception {
        OfaPayPropertyEntity payProperty = getPayProperty(PayConstants.OFA_PAY_TYPE,
                OfaPayPropertyEntity.class);

        return getSign(params, payProperty);

    }

    private String getSign(Map<String, String> params, OfaPayPropertyEntity payProperty) {
        String signString = MapUtil.getStringSortByKey(params, false, true);
        signString += "&key=" + payProperty.getKey();
        String sign = MD5Util.getMD5(signString);
        log.info(String.format("orderNo:%s,signString:%s,sign:%s", params.get("orderid"), signString,
                sign));

        return sign;
    }

    private String getSign(Map<String, String> params, OfaWithdrawPropertyEntity payProperty) {
        String signString = MapUtil.getStringSortByKey(params, false, true);
        signString += "&key=" + payProperty.getKey();
        String sign = MD5Util.getMD5(signString);
        log.info(String.format("orderNo:%s,signString:%s,sign:%s", params.get("orderid"), signString,
                sign));

        return sign;
    }

    private void setFinanceWithdrawOfaDto(FinanceWithdrawOfaDto dto,
                                          OfaWithdrawPropertyEntity proxpayWithdrawProperty) {

        if (proxpayWithdrawProperty.getBankDetails() != null) {

            Map<String, String> selectBankname = new HashMap<>(proxpayWithdrawProperty.getBankDetails().size());
            Map<String, String> selectBankno = new HashMap<>(proxpayWithdrawProperty.getBankDetails().size());
            proxpayWithdrawProperty.getBankDetails().forEach((k, v) -> {
                selectBankname.put(new String(v.getBytes(), StandardCharsets.UTF_8),
                        new String(v.getBytes(), StandardCharsets.UTF_8));

                selectBankno.put(new String(v.getBytes(),
                                StandardCharsets.UTF_8) + "-" + new String(k.getBytes(), StandardCharsets.UTF_8) + "-",
                        new String(k.getBytes(), StandardCharsets.UTF_8));

            });
            dto.setBanknameSelect(selectBankname);
            dto.setBanknoSelect(selectBankno);
        }
    }
}
