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.FinanceWithdrawAllLandDto;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.callback.AllLandPayCallbackEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawAllLandEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawInfoEntity;
import com.crm.pay.entity.property.AllLandPayPropertyEntity;
import com.crm.pay.entity.property.AllLandPayWithdrawPropertyEntity;
import com.crm.pay.service.AllLandPayService;
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.core.exception.PayValidatedException;
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.rely.backend.model.entity.finance.deposit.FinanceMerchantDepositAddEntity;
import com.crm.rely.backend.model.pojo.table.CustomInfoTable;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.FinanceWithdrawTable;
import com.crm.rely.backend.model.pojo.table.SysRemitChannelTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
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.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@Service
public class AllLandPayServiceImpl extends BasePayServiceImpl implements AllLandPayService {

    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private CustomInfoService customInfoService;

    private static String getSign(String apiKey, String apiSecret, Map<String, Object> map) throws Exception {
        //构建字符串
        String mapString = MapUtil.getStringSortByKey(MapUtil.convertToStringMap(map), false, true);
        String signString = mapString + "&api_secret=" + apiSecret;
        String sign = MD5Util.getMD5(signString).toUpperCase();

        log.info(String.format("加密处理--请求sign string:%s,sign:%s", signString, sign));
//        System.out.println(String.format("加密处理--请求sign string:%s,sign:%s", signString, sign));
        return sign;
    }

    public static void main(String[] args) throws Exception {
        String con = "{\n" +
                "    \"appSecret\":\"hs-Z5TVAQT2EPDWLSFKBC3YAQHXOR8BV\",\n" +
                "    \"appKey\":\"hk-WJMNYWUTHRKP5IUQCLAOQEICS8X0\",\n" +
                "    \"baseUrl\":\"https://api.alllandpay.cn/addons/huanyu\",\n" +
                "    \"withdrawUrl\":\"/merchant/createOrder\",\n" +
                "    \"notifyUrl\":\"https://ad.44a5c8109e4.com/allLand/withdraw/callback\",\n" +
                "}";
        System.out.println(AESUtil.encrypt(con, "bfa5559109f94c78af615bcf00d52060"));

        //String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, Object> map = new LinkedHashMap<>();

        map.put("order_type", 1);
        map.put("cny_amount", "200");
        map.put("paychannel_id", 1);
        map.put("paychannel_paymethod", "alipay");
        map.put("customer_name", "zhangsan");
        map.put("api_key", "hk-WJMNYWUTHRKP5IUQCLAOQEICS8X0");
        map.put("timestamp", DateUtil.getTimestamp());
        map.put("nonce", DateUtil.formatDateTime());

        String sign = getSign("hk-WJMNYWUTHRKP5IUQCLAOQEICS8X0", "hs-Z5TVAQT2EPDWLSFKBC3YAQHXOR8BV", map);

        map.put("signature", sign);

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

        //String url = "https://api.alllandpay.cn/addons/huanyu/merchant/createOrder";
        //Connection.Response response = HttpUtil.post(url,headers,map);
        //log.info(String.format("url:%s,json:%s,body:%s",url, JSON.toJSONString(map), response.body()));

    }

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

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

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

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("order_type", 1);
        map.put("cny_amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));

        String[] paychannelPaymethod = balanceSaveEntity.getBankCode().split("_");
        map.put("paychannel_id", Integer.valueOf(paychannelPaymethod[1]));
        map.put("paychannel_paymethod", paychannelPaymethod[0]);
        map.put("customer_name", balanceTable.getName());
        map.put("timestamp", DateUtil.getTimestamp());
        map.put("nonce", DateUtil.formatDateTime());
        map.put("api_key", payProperty.getApiKey());

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

    }

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

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

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

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("order_type", 1);
        map.put("cny_amount", String.format("%.2f", balanceTable.getTransformAmount().doubleValue()));

        String[] paychannelPaymethod = balanceSaveEntity.getBankCode().split("_");
        map.put("paychannel_id", Integer.valueOf(paychannelPaymethod[1]));
        map.put("paychannel_paymethod", paychannelPaymethod[0]);
        map.put("customer_name", balanceSaveEntity.getName());
        map.put("timestamp", DateUtil.getTimestamp());
        map.put("nonce", DateUtil.formatDateTime());
        map.put("api_key", payProperty.getApiKey());

        String url = getUrl(payProperty, map, balanceTable);
        return url;
    }

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

        if (Strings.isNullOrEmpty(callbackEntity.getSignature())) {
            return Constants.FAIL;
        }
        //获取配置
        AllLandPayPropertyEntity payProperty = getPayProperty(payType,
                AllLandPayPropertyEntity.class);

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

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

        FinanceDepositTable table = financeDepositService.getByChannelSerial(callbackEntity.getOrder_no());
        if (table == null) {
            return Constants.FAIL;
        }

        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(table.getSerial() + "_" + table.getSerial(),
                        callbackEntity.getOrder_no(),
                        status, callbackEntity);
        balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getMerchant_amount()));

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

    /**
     * 获取支付url
     *
     * @return
     * @throws ServiceException
     */
    private String getUrl(AllLandPayPropertyEntity propertyEntity, Map<String, Object> map,
                          FinanceDepositTable balanceTable) throws Exception {


        String sign = getSign(propertyEntity.getApiKey(), propertyEntity.getApiSecret(), map);
        map.put("signature", sign);

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

        Connection.Response response = HttpUtil.post(propertyEntity.getBaseUrl() + propertyEntity.getDepositUrl(),
                headers, 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") != 1) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        String dataString = requestEntity.getString("data");
        JSONObject dataEntity = JSON.parseObject(dataString);
        if (dataEntity == null || Strings.isNullOrEmpty(dataEntity.getString("order_no"))) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        String url = dataEntity.getString("pay_url");
        log.info(String.format("支付请求,请求orderNo:%s,请求url:%s", dataEntity.getString("order_no"), url));

        balanceTable.setChannelSerial(dataEntity.getString("order_no"));
        balanceTable.setFormUrl(url);
        financeDepositService.save(balanceTable);

        return url;
    }

    @Override
    public FinanceWithdrawAllLandDto 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.ALLLAND_PAY_TYPE_REMIT);

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

        FinanceWithdrawAllLandDto dto = new FinanceWithdrawAllLandDto();
        dto.setCnyAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setPaymethod(withdrawDto.getBankCode());

        if (!Strings.isNullOrEmpty(withdrawDto.getExtParam())) {

            JSONObject extParamJson = JSON.parseObject(withdrawDto.getExtParam());
            String identity = extParamJson.getString("identity");
            dto.setCertNo(identity);
        } else {
            CustomInfoTable table = customInfoService.getByCId(withdrawDto.getCId());
            dto.setCertNo(table.getIdentity());
        }


        dto.setBankCardNum(withdrawDto.getBankCardNum());
        dto.setBankName(withdrawDto.getBankName());
        dto.setBankUname(withdrawDto.getBankUname());

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

        AllLandPayWithdrawPropertyEntity propertyEntity = getWithdrawProperty(PayConstants.ALLLAND_PAY_TYPE_REMIT,
                AllLandPayWithdrawPropertyEntity.class);

        String channelSerial = payout(entity, propertyEntity);

        FinanceWithdrawApproveChannelSubmitEntity financeWithdrawApproveChannelSubmitEntity =
                new FinanceWithdrawApproveChannelSubmitEntity();
        financeWithdrawApproveChannelSubmitEntity.setSubmitStatus(PayConstants.PASSED_STATUS);
        financeWithdrawApproveChannelSubmitEntity.setId(entity.getId());
        financeWithdrawApproveChannelSubmitEntity.setActualPayType(entity.getPayType());
        financeWithdrawApproveChannelSubmitEntity.setChannelSerial(channelSerial);
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);

        } else {
           //financeAgentWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);
        }

        return null;
    }

    private String payout(FinanceWithdrawAllLandEntity entity,
                          AllLandPayWithdrawPropertyEntity propertyEntity) throws Exception {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("order_type", 2);
        map.put("cny_amount", String.format("%.2f", entity.getCnyAmount().doubleValue()));

        String[] paychannelPaymethod = entity.getPayType().split("_");
        map.put("paychannel_id", Integer.valueOf(paychannelPaymethod[0]));
        map.put("paychannel_paymethod", paychannelPaymethod[1]);
        map.put("api_key", propertyEntity.getApiKey());
        map.put("timestamp", DateUtil.getTimestamp());
        map.put("nonce", DateUtil.formatDateTime());

        Map<String, Object> huifuPersonalData = new LinkedHashMap<>();
        huifuPersonalData.put("name", entity.getBankUname());
        huifuPersonalData.put("cert_type", "00");
        huifuPersonalData.put("cert_no", "1");
        huifuPersonalData.put("cert_validity_type", "00");
        huifuPersonalData.put("cert_begin_date", "19800504");
        huifuPersonalData.put("mobile_no", "1");

        Map<String, Object> huifuCardData = new LinkedHashMap<>();
        huifuCardData.put("card_type", "1");
        huifuCardData.put("card_name", entity.getBankUname());
        huifuCardData.put("card_no", entity.getBankCardNum());
        huifuCardData.put("prov_id", "310000");
        huifuCardData.put("area_id", "310000");


        map.put("huifu_personal_data", huifuPersonalData);
        map.put("huifu_card_info", huifuCardData);

        String sign = getSign(propertyEntity.getApiKey(), propertyEntity.getApiSecret(), map);
        map.put("signature", sign);

        Map<String, String> headers = new HashMap<>(1);
        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(), JSON.toJSONString(map),
                response.body()));

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

        String dataString = requestEntity.getString("data");
        JSONObject dataEntity = JSON.parseObject(dataString);
        if (dataEntity == null || Strings.isNullOrEmpty(dataEntity.getString("order_no"))) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        return dataEntity.getString("order_no");

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdrawCallback(Integer withdrawType, AllLandPayCallbackEntity callbackEntity) throws Exception {
        log.info(String.format("callback data:%s", JSON.toJSONString(callbackEntity)));
        if (Strings.isNullOrEmpty(callbackEntity.getSignature())) {
            return Constants.FAIL;
        }
        //获取配置
        AllLandPayWithdrawPropertyEntity payProperty = getWithdrawProperty(PayConstants.ALLLAND_PAY_TYPE_REMIT,
                AllLandPayWithdrawPropertyEntity.class);

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

        String serial = null;
        if (withdrawType == null || withdrawType.equals(1)) {
            FinanceWithdrawTable table = financeWithdrawService.getByChannelSerial(callbackEntity.getOrder_no());
            if (table == null) {
                return Constants.FAIL;
            }
            serial = table.getSerial();
        } else {
           /* FinanceAgentWithdrawTable table =
                    financeAgentWithdrawService.getByChannelSerial(callbackEntity.getOrder_no());
            if (table == null) {
                return Constants.FAIL;
            }
            serial = table.getSerial();*/
        }

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

        if ("confirmed".equals(callbackEntity.getStatus()) || "completed".equals(callbackEntity.getStatus())) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_SUCCESS_STATUS);
        } else if ("cancelled".equals(callbackEntity.getStatus())) {
            financeWithdrawApproveChannelEntity.setCallbackStatus(PayConstants.CALLBACE_FAIL_STATUS);
        }

        financeWithdrawApproveChannelEntity.setSerial(serial);

        /**
         * 更新数据
         */

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

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

        return Constants.SUCCESS;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(AllLandPayCallbackEntity callbackEntity, String apiKey, String apiSecret) throws Exception {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("merchant_id", callbackEntity.getMerchant_id());
        map.put("order_id", callbackEntity.getOrder_id());
        map.put("order_no", callbackEntity.getOrder_no());
        map.put("order_type", callbackEntity.getOrder_type());
        map.put("status", callbackEntity.getStatus());
        map.put("cny_amount", callbackEntity.getCny_amount());
        map.put("merchant_amount", callbackEntity.getMerchant_amount());
        map.put("merchant_actual_amount", callbackEntity.getMerchant_actual_amount());
        map.put("service_amount", callbackEntity.getService_amount());
        map.put("createtime", callbackEntity.getCreatetime());
        map.put("updatetime", callbackEntity.getUpdatetime());
        map.put("timestamp", callbackEntity.getTimestamp());
        map.put("nonce", callbackEntity.getNonce());

        String sign = getSign(apiKey, apiSecret, map);

        String _sign = callbackEntity.getSignature();

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


}



