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.FinanceWithdrawPartnerTwDto;
import com.crm.pay.entity.callback.PartnerTwPayCallbackEntity;
import com.crm.pay.entity.property.PartnerTwPayPropertyEntity;
import com.crm.pay.entity.property.PartnerTwWithdrawPropertyEntity;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.PartnerTwPayService;
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.FinanceWithdrawPartnerTwEntity;
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.*;
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.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@Service
public class PartnerTwPayServiceImpl extends BasePayServiceImpl implements PartnerTwPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.PARTNER_TW_PAY_KEY);
        //通道配置实体
        PartnerTwPayPropertyEntity payProperty = getPayProperty(channel, PartnerTwPayPropertyEntity.class);

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

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

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

    }

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

        if (Strings.isNullOrEmpty(callbackEntity.getSign())) {
            return Constants.FAIL;
        }
        //获取配置
        PartnerTwPayPropertyEntity payProperty = getPayProperty(PayConstants.PARTNER_TW_PAY_KEY, PartnerTwPayPropertyEntity.class);

        boolean validate = validate(callbackEntity, payProperty);
        if (!validate) {
            return Constants.FAIL;
        }

        //验证传输数据
        int status = PayConstants.CALLBACE_PROCESSING_STATUS;
        if ("PAID".equals(callbackEntity.getStatus())) {
            status = PayConstants.CALLBACE_SUCCESS_STATUS;
        } else if ("MANUAL PAID".equals(callbackEntity.getStatus())) {
            status = PayConstants.CALLBACE_FAIL_STATUS;
        }

        Map<String, String> map = new LinkedHashMap<>();
        map.put("merchantNo", payProperty.getMerchantNo());
        map.put("tradeNo", callbackEntity.getTradeNo());
        map.put("orderNo", callbackEntity.getOrderNo());
        map.put("time", String.valueOf(System.currentTimeMillis()));
        String sign = getSign(payProperty.getAppKey(), payProperty.getAppSecret(), map);

        map.put("appSecret", payProperty.getAppSecret());
        map.put("sign", sign);

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

        Connection.Response response = HttpUtil.post(headers, payProperty.getBaseUrl() + payProperty.getDepositQueryUrl(), map);
        log.info(String.format("url:%s,request:%s,body:%s", payProperty.getDepositQueryUrl(), JSON.toJSONString(map), response.body()));

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (requestEntity == null || Strings.isNullOrEmpty(requestEntity.getString("status")) || requestEntity.getString("status").equals("PENDING")) {
            log.error("Payment platform is abnormal, please try again later or contact customer service.");
            return Constants.FAIL;
        }

        if (!callbackEntity.getTradeNo().equals(requestEntity.getString("tradeNo"))
                || !callbackEntity.getOrderNo().equals(requestEntity.getString("orderNo")) || new BigDecimal(callbackEntity.getPaid()).compareTo(new BigDecimal(requestEntity.getFloat("paid"))) != 0) {
            return Constants.FAIL;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(callbackEntity.getOrderNo(), callbackEntity.getTradeNo(),
                        status, callbackEntity);
        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getPaid()));

        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);
        return Constants.SUCCESS;
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(PartnerTwPayPropertyEntity propertyEntity, FinanceDepositTable balanceTable,
                          FinanceDepositAddEntity balanceSaveEntity) throws Exception {
        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();

        Map<String, String> map = new LinkedHashMap<>();
        map.put("merchantNo", propertyEntity.getMerchantNo());
        map.put("orderNo", orderNo);
        map.put("userNo", String.valueOf(balanceTable.getCId()));
        map.put("amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));
        map.put("discount", "0.00");
        map.put("extra", "");
        map.put("datetime", DateUtil.formatTime(balanceTable.getAddTime()));
        map.put("notifyUrl", propertyEntity.getNotifyUrl());
        map.put("time", String.valueOf(System.currentTimeMillis()));

        String sign = getSign(propertyEntity.getAppKey(), propertyEntity.getAppSecret(), map);
        map.put("userName", balanceTable.getName());
        map.put("channelNo", "1");
        map.put("appSecret", propertyEntity.getAppSecret());
        map.put("sign", sign);

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

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

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (requestEntity == null || requestEntity.getInteger("code") == null || requestEntity.getInteger("code") != 0
                || Strings.isNullOrEmpty(requestEntity.getString("text")) || !requestEntity.getString("text").equals("success")) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" + " service.");
        }

        String url = requestEntity.getString("targetUrl");
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", map.get("merchantOrderNo"), url));

        return url;
    }

    @Override
    public FinanceWithdrawPartnerTwDto 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.PARTNER_TW_PAY_TYPE_REMIT);

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

        FinanceWithdrawPartnerTwDto dto = new FinanceWithdrawPartnerTwDto();
        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setName(withdrawDto.getName());
        dto.setBankName(withdrawDto.getBankUname());
        dto.setBankBranch(withdrawDto.getBankBranchName());
        dto.setBankAccount(withdrawDto.getBankCardNum());

        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(FinanceWithdrawPartnerTwEntity 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());

        PartnerTwWithdrawPropertyEntity proxpayWithdrawProperty =
                getWithdrawProperty(PayConstants.PARTNER_TW_PAY_TYPE_REMIT, PartnerTwWithdrawPropertyEntity.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(FinanceWithdrawPartnerTwEntity entity, PartnerTwWithdrawPropertyEntity propertyEntity) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("merchantNo", propertyEntity.getMerchantNo());
        map.put("orderNo", entity.getSerial());
        map.put("amount", String.format("%.2f", entity.getAmount().doubleValue()));
        map.put("name", entity.getName());
        map.put("bankName", entity.getBankName());
        map.put("bankAccount", entity.getBankAccount());
        map.put("mobile", Strings.isNullOrEmpty(entity.getMobile()) ? entity.getMobile() : "");
        map.put("datetime", DateUtil.formatTime(new Date()));
        map.put("extra", "");
        map.put("time", String.valueOf(System.currentTimeMillis()));
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            map.put("notifyUrl", propertyEntity.getNotifyUrl());
            map.put("reverseUrl", propertyEntity.getNotifyUrl());
        } else {
            map.put("notifyUrl", propertyEntity.getAgentNotifyUrl());
            map.put("reverseUrl", propertyEntity.getAgentReverseUrl());
        }
        String sign = getSign(propertyEntity.getAppKey(), propertyEntity.getAppSecret(), map);
        map.put("memo", entity.getMemo());
        map.put("bankBranch", entity.getBankBranch());
        map.put("appSecret", propertyEntity.getAppKey());
        map.put("sign", sign);

        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        Connection.Response response = HttpUtil.post(headers,
                propertyEntity.getBaseUrl() + propertyEntity.getWithdrawUrl(),
                map);
        log.info(String.format("url:%s,json:%s,body:%s",
                propertyEntity.getBaseUrl() + propertyEntity.getWithdrawQueryUrl(), JSON.toJSONString(map),
                response.body()));

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (requestEntity == null || requestEntity.getInteger("code") == null || requestEntity.getInteger("code") != 0
                || Strings.isNullOrEmpty(requestEntity.getString("text")) || !requestEntity.getString("text").equals(
                "success")) {
            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, PartnerTwPayCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback data:%s", JSON.toJSONString(callbackEntity)));
        if (Strings.isNullOrEmpty(callbackEntity.getSign())) {
            return Constants.FAIL;
        }
        //获取配置
        PartnerTwWithdrawPropertyEntity payProperty = getWithdrawProperty(PayConstants.PARTNER_TW_PAY_TYPE_REMIT,
                PartnerTwWithdrawPropertyEntity.class);

        boolean validateWith = validateWith(callbackEntity, payProperty);
        if (!validateWith) {
            return Constants.FAIL;
        }

        Map<String, String> map = new LinkedHashMap<>();
        map.put("merchantNo", payProperty.getMerchantNo());
        map.put("tradeNo", callbackEntity.getTradeNo());
        map.put("orderNo", callbackEntity.getOrderNo());
        map.put("time", String.valueOf(System.currentTimeMillis()));
        String sign = getSign(payProperty.getAppKey(), payProperty.getAppSecret(), map);

        map.put("appSecret", payProperty.getAppSecret());
        map.put("sign", sign);

        Map<String, String> headers = new HashMap<>(1);
        headers.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        Connection.Response response = HttpUtil.post(headers, payProperty.getBaseUrl() + payProperty.getWithdrawUrl()
                , map);
        log.info(String.format("url:%s,request:%s,body:%s",
                payProperty.getBaseUrl() + payProperty.getWithdrawQueryUrl(), JSON.toJSONString(map), response.body()));

        JSONObject requestEntity = JSON.parseObject(response.body());
        if (requestEntity == null || requestEntity.getInteger("code") == null || requestEntity.getInteger("code") != 0
                || Strings.isNullOrEmpty(requestEntity.getString("status")) || !requestEntity.getString("status").equals(
                "PAID")) {
            log.error("Payment platform is abnormal, please try again later or contact customer service.");
            return Constants.FAIL;
        }

        if (!callbackEntity.getTradeNo().equals(requestEntity.getString("tradeNo")) || !callbackEntity.getStatus().equals(requestEntity.getString("status"))
                || !callbackEntity.getOrderNo().equals(requestEntity.getString("orderNo"))) {
            return Constants.FAIL;
        }


        FinanceWithdrawApproveChannelEntity financeWithdrawApproveChannelEntity =
                new FinanceWithdrawApproveChannelEntity();
        financeWithdrawApproveChannelEntity.setChannelSerial(callbackEntity.getTradeNo());

        if ("PAID".equals(callbackEntity.getStatus())) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_SUCCESS_STATUS);
        } else if ("CANCELLED".equals(callbackEntity.getStatus()) || "REVERSED".equals(callbackEntity.getStatus())) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        }
        financeWithdrawApproveChannelEntity.setSerial(callbackEntity.getTradeNo());

        /**
         * 更新数据
         */

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

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

        return Constants.SUCCESS;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(PartnerTwPayCallbackEntity callbackEntity, PartnerTwPayPropertyEntity payProperty) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("status", callbackEntity.getStatus());
        map.put("tradeNo", callbackEntity.getTradeNo());
        map.put("orderNo", callbackEntity.getOrderNo());
        map.put("userNo", callbackEntity.getUserNo());
        map.put("amount", String.valueOf(callbackEntity.getAmount()));
        map.put("discount", callbackEntity.getDiscount());
        map.put("extra", Strings.isNullOrEmpty(callbackEntity.getExtra()) ? "" : callbackEntity.getExtra());
        map.put("lucky", callbackEntity.getLucky());
        map.put("paid", callbackEntity.getPaid());

        String sign = getSign(payProperty.getAppKey(), payProperty.getAppSecret(), map);

        String _sign = callbackEntity.getSign();

        log.info(String.format("order no:%scallback result sign compare--callback sign:%s, encryption sign result:%s"
                , callbackEntity.getTradeNo(), _sign, sign));
        return sign.equals(_sign);
    }

    private boolean validateWith(PartnerTwPayCallbackEntity callbackEntity, PartnerTwWithdrawPropertyEntity payProperty) throws Exception {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("status", callbackEntity.getStatus());
        map.put("tradeNo", callbackEntity.getTradeNo());
        map.put("orderNo", callbackEntity.getOrderNo());
        map.put("amount", String.valueOf(callbackEntity.getAmount()));
        map.put("name", callbackEntity.getName());
        map.put("bankName", callbackEntity.getBankName());
        map.put("bankAccount", callbackEntity.getBankAccount());
        //map.put("bankBranch", callbackEntity.getBankBranch());
        //map.put("memo", callbackEntity.getMemo());
        map.put("mobile", Strings.isNullOrEmpty(callbackEntity.getMobile()) ? "" : callbackEntity.getMobile());
        //map.put("fee", callbackEntity.getFee());
        map.put("extra", Strings.isNullOrEmpty(callbackEntity.getExtra()) ? "" : callbackEntity.getExtra());

        String sign = getSign(payProperty.getAppKey(), payProperty.getAppSecret(), map);

        String _sign = callbackEntity.getSign();

        log.info(String.format("order no:%scallback result sign compare--callback sign:%s, encryption sign result:%s"
                , callbackEntity.getTradeCode(), _sign, sign));
        return sign.equals(_sign);
    }

    private static String getSign(String appKey, String appSecret, Map<String, String> map) throws Exception {
        //构建字符串
        String mapString = MapUtil.getStringSortByKey(map, false, true);
        String signString = mapString + appKey;

//        //URL编码 转小写
//        String mapEncoderString = URLEncoder.encode(signString).toLowerCase();
//
//        //URL编码
//        String signEncoderString = URLEncoder.encode(mapEncoderString);

        //对生成的字符串使用HMAC-SHA256进行加密
        String signHMACSHA256 = ShaUtil.sha256(signString);

        String sign = MD5Util.getMD5(signHMACSHA256).toUpperCase();

        log.info(String.format("加密处理--请求orderNo:%s,sign string:%s,sign:%s", map.get("orderNo"), signString, sign));

        return sign;
    }

    public static void main(String[] args) throws Exception {
        /*String con = "{\n" +
                "    \"appSecret\":\" fW6SxMShlUiUd7jCsYODmw\",\n" +
                "    \"appKey\":\"CtCYKjZiC0OqqwzjmcjMiA\",\n" +
                "    \"baseUrl\":\"https://yx.foeniks.cc/\",\n" +
                "    \"depositUrl\":\"/order/create\",\n" +
                "    \"depositQueryUrl\":\"/order/status\",\n" +
                "    \"notifyUrl\":\"https://ad.44a5c8109e4.com/partner/tw/withdraw/callback\",\n" +
                "    \"merchantNo\":\"878624\",\n" +
                "}";*/
        String con = "{\n" +
                "    \"appSecret\":\"fW6SxMShlUiUd7jCsYODmw\",\n" +
                "    \"appKey\":\"CtCYKjZiC0OqqwzjmcjMiA\",\n" +
                "    \"baseUrl\":\" https://yx.foeniks.cc/\",\n" +
                "    \"withdrawUrl\":\"/payout/create\",\n" +
                "    \"withdrawQueryUrl\":\"/payout/status\",\n" +
                "    \"notifyUrl\":\"https://ad.44a5c8109e4.com/partner/tw/withdraw/callback\",\n" +
                "    \"agentNotifyUrl\":\"https://ad.44a5c8109e4.com/agent/partner/tw/withdraw/callback\",\n" +
                "    \"reverseUrl\":\"https://ad.44a5c8109e4.com/partner/tw/withdraw/callback/reverse\",\n" +
                "    \"agentReverseUrl\":\"https://ad.44a5c8109e4.com/agent/partner/tw/withdraw/callback/reverse\",\n" +
                "    \"merchantNo\":\"878624\",\n" +
                "}";
        System.out.println(AESUtil.encrypt(con, "bfa5559109f94c78af615bcf00d52060"));

        //String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        String orderNo = "20250324234323424";
        Map<String, String> map = new LinkedHashMap<>();
        map.put("status","PAID");
        map.put("tradeNo","4356343534564");
        map.put("orderNo","20250324234323424");
        map.put("userNo","7264584");
        map.put("amount", "102.00");
        map.put("discount","0.00");
        map.put("extra","");
        map.put("lucky", "0.00");
        map.put("paid", "102.00");

       /* map.put("status", "PAID");
        map.put("tradeNo", "4356343534564");
        map.put("orderNo", "20250324234323424");
        map.put("amount", "102.00");
        map.put("name", "eterte");
        map.put("bankName", "dfgsfsdert");
        map.put("bankAccount", "45646456454564536");
        map.put("mobile", "");
        map.put("extra", "");*/

        String sign = getSign("VHRefe7I0mlJb4GfUJcKw", "95DJG8jkrEyOp8kSRx7E1Q", map);
        System.out.println(sign);

        //Connection.Response response = HttpUtil.post("https://documenter.getpostman.com/api/PartnerTw/deposit-orders", data);
        //System.out.println("result:" + JSON.parseObject(response.body()).toJSONString());

    }

}



