package com.gree.mall.manager.logic.common;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.profitsharing.*;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.ProfitSharingService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.gree.mall.manager.config.wx.WxConfiguration;
import com.gree.mall.manager.exception.RemoteServiceException;
import com.gree.mall.manager.plus.entity.OrderInfo;
import com.gree.mall.manager.plus.entity.OrderShare;
import com.gree.mall.manager.utils.ArithUtils;
import com.gree.mall.manager.utils.HttpUtils;
import com.gree.mall.manager.utils.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.apache.commons.lang3.StringUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by lijh on 19/06/28.
 */

@Component
@Slf4j
@RequiredArgsConstructor
public class WechatLogic {

    public WxPayService getPayService(String companyWechatId){
        return WxConfiguration.wxPayServices.get(companyWechatId);
    }
    public WxMaService getMaService(String companyWechatId){
        return WxConfiguration.wxMaService.get(companyWechatId);
    }

    /**
     * 获取微信token
     *
     * @return
     * @throws RemoteServiceException
     */
    public String getAccessToken(String companyWechatId) throws RemoteServiceException, WxErrorException {
        return this.getMaService(companyWechatId).getAccessToken();
    }

    /**
     * 发货到微信
     * @param orderInfo
     */
    public void deliver(OrderInfo orderInfo,String openId) throws WxErrorException {
        String token = this.getAccessToken(orderInfo.getPayWorkerCodeId());
        String url = "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info?access_token="+token;
        Map<String,Object> map = new HashMap<>();
        map.put("access_token",token);
        map.put("logistics_type",1);
        map.put("delivery_mode","UNIFIED_DELIVERY");
        map.put("upload_time",new Date());

        List<Map<String,Object>> list = new ArrayList<>();
        Map<String,Object> shoppingMap = new HashMap<>();
        shoppingMap.put("item_desc",orderInfo.getOrderTitle());
        list.add(shoppingMap);
        map.put("shipping_list",list);

        Map<String,String> payerMap = new HashMap<>();
        payerMap.put("openid",openId);
        map.put("payer",payerMap);

        log.info("【对接微信发货】request:{}",JSONObject.toJSONString(map));
        String s = HttpUtils.requestPostBody(url, JSONObject.toJSONString(map), null);
        log.info("【对接微信发货】response:{}",s);
    }

    /**
     * 服务商退款
     */
    public void refund(String orderId, String refundNo, Double totalFee, Double refundFee,String companyWechatId) throws  RemoteServiceException {
        WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
        wxPayRefundRequest.setOutTradeNo(orderId);
        wxPayRefundRequest.setOutRefundNo(refundNo);
        wxPayRefundRequest.setRefundFee((int) ArithUtils.mul(refundFee, 100));
        wxPayRefundRequest.setTotalFee((int) ArithUtils.mul(totalFee, 100));
        wxPayRefundRequest.setNonceStr(StringUtil.getUUID());
//        wxPayRefundRequest.setNotifyUrl(refundNotifyUrl);

        log.info("【申请微信退款】request=" + JSONObject.toJSONString(wxPayRefundRequest));
        try {
            WxPayRefundResult refund = this.getPayService(companyWechatId).refund(wxPayRefundRequest);
            log.info("【申请微信退款】result=" + JSONObject.toJSONString(refund));

            if (!StringUtils.equals(refund.getReturnCode(), "SUCCESS")) {
                throw new RemoteServiceException(refund.getReturnCode() + refund.getReturnMsg());
            }
        }catch (WxPayException ex){
            throw new RemoteServiceException("退款失败："+ex.getMessage());
        }


    }


    /**
     * 分账
     *
     * @param orderId
     * @param transactionId
     * @param orderShares
     * @throws WxPayException
     */
    public void shareAmount(String orderId, String transactionId, List<OrderShare> orderShares, String companyWechatId) throws WxPayException {
        //开始分账
        profitSharing(orderId, transactionId, orderShares, companyWechatId);
    }

    private void profitSharing(String orderSharingId, String transactionId, List<OrderShare> orderShares, String companyWechatId) throws WxPayException {

        log.info("【准备结算】:" + JSONArray.toJSONString(orderShares));
        orderShares = orderShares.stream().filter(v -> v.getAmount().doubleValue() > 0).collect(Collectors.toList());
        log.info("【准备结算2】:" + JSONArray.toJSONString(orderShares));
        if (CollectionUtil.isEmpty(orderShares)) {
            return;
        }

        List<String> openIds = orderShares.stream().map(OrderShare::getOpenId).distinct().collect(Collectors.toList());
        List<Map<String, Object>> list = new ArrayList<>();

        for (String openId : openIds) {
            //添加分账人
            addShareReveiver(openId, companyWechatId);

            BigDecimal shareAmount = orderShares.stream().filter(v -> v.getOpenId().equals(openId)).map(OrderShare::getAmount)
                    .reduce(BigDecimal::add).orElse(BigDecimal.valueOf(0));
            String workerUserPhone = orderShares.stream().filter(v -> v.getOpenId().equals(openId)).map(OrderShare::getWorkUserPhone).collect(Collectors.toList()).get(0);
            if (shareAmount.doubleValue() == 0) {
                continue;
            }
            Map<String, Object> receiver = new HashMap<>();
            receiver.put("type", "PERSONAL_SUB_OPENID");
            receiver.put("account", openId);
            receiver.put("amount", ProfitSharingReceiverRequest.yuanToFen(shareAmount.toString()));
            receiver.put("description", "销售返佣给" + workerUserPhone + "，金额：" + shareAmount + "元");
            list.add(receiver);
        }

        ProfitSharingRequest profitSharingRequest = new ProfitSharingRequest();
        profitSharingRequest.setOutOrderNo(orderSharingId);
        profitSharingRequest.setTransactionId(transactionId);
        profitSharingRequest.setReceivers(JSONObject.toJSONString(list));

        ProfitSharingService profitSharingService = this.getPayService(companyWechatId).getProfitSharingService();
        //请求单次分账
        log.info("【开始分账】request:{}", JSONObject.toJSONString(profitSharingRequest));
        ProfitSharingResult profitSharingResult = profitSharingService.profitSharing(profitSharingRequest);
        log.info("【开始分账】result:{}", JSONObject.toJSONString(profitSharingResult));

    }

    public ProfitSharingQueryResult profitSharingResult(OrderShare orderShare) throws WxPayException {
        if (StringUtils.isBlank(orderShare.getCompanyWechatId())) {
            return null;
        }
        ProfitSharingService profitSharingService = this.getPayService(orderShare.getCompanyWechatId()).getProfitSharingService();

        ProfitSharingQueryRequest profitSharingRequest = new ProfitSharingQueryRequest();
        profitSharingRequest.setOutOrderNo(orderShare.getOrderId());
        profitSharingRequest.setTransactionId(orderShare.getTransactionId());
        // 请求查询分账结果
        log.info("【查询分账结果】request:{}", JSONObject.toJSONString(profitSharingRequest));
        ProfitSharingQueryResult profitSharingQueryResult = profitSharingService.profitSharingQuery(profitSharingRequest);
        log.info("【查询分账结果】result:{}", JSONObject.toJSONString(profitSharingQueryResult));
        return profitSharingQueryResult;
    }

    private boolean addShareReveiver(String openId,String companyWechatId) {
        Map<String, Object> receiver = new HashMap<>();
        /**
         *  MERCHANT_ID:商户号
         *  PERSONAL_OPENID:个人openId(由父商户号APPID转换得到)
         *  PERSONAL_SUB_OPENID:个人sub_openid（由子商户APPID转换得到）
         */
        receiver.put("type", "PERSONAL_SUB_OPENID");
        /**
         * 分账接收方类型为MERCHANT_ID时，分账接收方账号为商户号
         * 分账接收方类型为PERSONAL_OPENID时，分账接收方账号为个人openid
         * 分账接收方类型为PERSONAL_SUB_OPENID时，分账接收方账号为个人sub_openid
         */
        receiver.put("account", openId);
        //receiver.put("name",name);
        receiver.put("relation_type", "STAFF");
        //添加分账人
        ProfitSharingService profitSharingService = this.getPayService(companyWechatId).getProfitSharingService();

        ProfitSharingReceiverRequest profitSharingReceiverRequest = new ProfitSharingReceiverRequest();
        profitSharingReceiverRequest.setReceiver(JSONObject.toJSONString(receiver));
        ProfitSharingReceiverResult result = null;
        try {
            result = profitSharingService.addReceiver(profitSharingReceiverRequest);
            if (result.getReturnCode().equals("SUCCESS")) {
                return true;

            }
        } catch (WxPayException e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 方法用途: 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序），并且生成url参数串<br>
     * 实现步骤: <br>
     *
     * @param paraMap    要排序的Map对象
     * @param urlEncode  是否需要URLENCODE
     * @param keyToLower 是否需要将Key转换为全小写
     *                   true:key转化成小写，false:不转化
     * @return
     */
    private static String formatUrlMap(Map<String, String> paraMap, boolean urlEncode, boolean keyToLower) {
        String buff = "";
        Map<String, String> tmpMap = paraMap;
        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<Map.Entry<String, String>>(tmpMap.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
            Collections.sort(infoIds, new Comparator<Map.Entry<String, String>>() {
                @Override
                public int compare(Map.Entry<String, String> o1, Map.Entry<String, String> o2) {
                    return (o1.getKey()).toString().compareTo(o2.getKey());
                }
            });
            // 构造URL 键值对的格式
            StringBuilder buf = new StringBuilder();
            for (Map.Entry<String, String> item : infoIds) {
                if (StringUtils.isNotBlank(item.getKey())) {
                    String key = item.getKey();
                    String val = item.getValue();
                    if (urlEncode) {
                        val = URLEncoder.encode(val, "utf-8");
                    }
                    if (keyToLower) {
                        buf.append(key.toLowerCase() + "=" + val);
                    } else {
                        buf.append(key + "=" + val);
                    }
                    buf.append("&");
                }

            }
            buff = buf.toString();
            if (buff.isEmpty() == false) {
                buff = buff.substring(0, buff.length() - 1);
            }
        } catch (Exception e) {
            return null;
        }
        return buff;
    }


    //xml解析
    public static Map doXMLParse(String strxml) throws JDOMException, IOException {
        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
        if (null == strxml || "".equals(strxml)) {
            return null;
        }

        Map m = new HashMap();
        InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List list = root.getChildren();
        for (Object aList : list) {
            Element e = (Element) aList;
            String k = e.getName();
            String v = "";
            List children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }
            m.put(k, v);
        }
        //关闭流
        in.close();
        return m;
    }

    private static String getChildrenText(List children) {
        StringBuilder sb = new StringBuilder();
        if (!children.isEmpty()) {
            for (Object aChildren : children) {
                Element e = (Element) aChildren;
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<").append(name).append(">");
                if (!list.isEmpty()) {
                    sb.append(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</").append(name).append(">");
            }
        }

        return sb.toString();
    }


    public String queryMaxShareRate(String id) throws WxPayException {
        final ProfitSharingService sharingService = this.getPayService(id).getProfitSharingService();
        ProfitSharingMerchantRatioQueryRequest request = new ProfitSharingMerchantRatioQueryRequest();
        final ProfitSharingMerchantRatioQueryResult result = sharingService.profitSharingMerchantRatioQuery(request);
        if (!result.getReturnCode().equals("SUCCESS")) {
            return "";
        }
        Map<String, Object> repMap = new HashMap<>();
        repMap.put("商户号", result.getMchId());
        repMap.put("子商户号", result.getSubMchId());
        repMap.put("最大分账比例", result.getMaxRatio());
        return JSONUtil.toJsonStr(repMap);
    }
}
