package com.ylx.massage.utils;

/**
 * @author jianlong
 * @date 2024-06-04 10:13
 */

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.ylx.common.config.WechatAccountConfig;
import com.ylx.common.constant.MassageConstants;
import com.ylx.common.core.redis.RedisCache;
import com.ylx.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


/**
 * 微信登录工具类
 */
@Component
@Slf4j
public class WeChatUtil {

    private final static String ERROR_CODE = "errcode";

    @Resource
    private WechatAccountConfig wxPayProperties;


    @Autowired
    private RedisCache redisCache;


    /**
     * 获取微信授权code
     *
     * @param state state信息
     * @return String 返回微信授权code的URL
     */
    public String getCode(String state) {
        try {
            StringBuffer url = new StringBuffer();
            url.append(wxPayProperties.getGetCodeUrl())
                    .append("?appid=")
                    .append(wxPayProperties.getMpAppId())
                    .append("&redirect_uri=")
                    .append(URLEncoder.encode(wxPayProperties.getRedirectUrl(), "UTF-8"))
//                    .append(URLEncoder.encode("https://city.baoxianzhanggui.com/fragrance/#/pages/my/wxLogin", "UTF-8"))
                    .append("&response_type=code&scope=snsapi_userinfo&state=")
                    .append("STATE")
                    .append("#wechat_redirect");
            return url.toString();
        } catch (UnsupportedEncodingException e) {
            throw new ServiceException("URL格式化异常");
        }

    }

    /**
     * 获取微信AccessToken
     *
     * @param code 用户code
     * @return Map<?, ?> 返回包含微信AccessToken的Map
     */
    public Map<?, ?> getAccessToken(String code) {
         StringBuffer url = new StringBuffer();
        url.append(wxPayProperties.getAccessTokenUrl())
                .append("?appid=").append(wxPayProperties.getMpAppId())
                .append("&secret=").append(wxPayProperties.getMpAppSecret())
                .append("&code=").append(code)
                .append("&grant_type=authorization_code");
        log.info("URL的值：{}", url.toString());
        String rs = HttpUtil.get(url.toString());
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        if (null == map.get(ERROR_CODE)) {
            return map;
        } else {
            log.error("getAccessToken-获取access_token出错:{}", map);
            throw new ServiceException("获取access_token出错");
        }
    }

    /**
     * 获取微信AccessToken
     *
     * @return String 返回包含微信AccessToken的Map
     */
    public String getToken() {
        if (redisCache.getCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId()) != null) {
            return redisCache.getCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId());
        }

        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/cgi-bin/token")
                .append("?grant_type=").append("client_credential")
                .append("&appid=").append(wxPayProperties.getMpAppId())
                .append("&secret=").append(wxPayProperties.getMpAppSecret());
        log.info("URL的值：{}", url.toString());
        String rs = HttpUtil.get(url.toString());
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        log.info("map的值：{}", map);
        if (null == map.get(ERROR_CODE)) {
            //key值：access_token:appid，缓存1小时
            redisCache.setCacheObject(MassageConstants.ACCESS_TOKEN_KEY + wxPayProperties.getMpAppId(), map.get("access_token").toString(), 1, TimeUnit.HOURS);
            return map.get("access_token").toString();
        } else {
            log.error("getToken-获取access_token出错:{}", map);
            throw new ServiceException("getToken-获取access_token出错");
        }
    }


    /**
     * 获取新增关注数量
     *
     * @return 返回包含增关注数量
     */
    public String getFollowers(String beginDate, String endDate) {
        String token = this.getToken();

        String urlStr = "https://api.weixin.qq.com/datacube/getusersummary?access_token=" + token;
        cn.hutool.json.JSONObject param1 = JSONUtil.createObj();
        param1.set("begin_date", beginDate);
        param1.set("end_date", endDate);
        String result = HttpUtil.post(urlStr, param1.toString());

        log.info("getFollowers-获取关注量请求参数:{}", param1);

        cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(result);
        log.info("getFollowers-获取关注量返回:{}", jsonObject);

        Object listObj = jsonObject.get("list");
        if (listObj == null || JSONUtil.isNull(listObj)) {
            return "0";
        }

        JSONArray list;
        try {
            if (listObj instanceof JSONArray) {
                list = (JSONArray) listObj;
            } else if (listObj instanceof String) {
                list = JSONUtil.parseArray(listObj.toString());
            } else if (listObj.getClass().getName().contains("ArrayList")) {
                // 如果是 FastJSON 返回的 ArrayList，转换为 JSON 字符串后再解析
                String jsonArrayStr = JSONUtil.toJsonStr(listObj);
                list = JSONUtil.parseArray(jsonArrayStr);
            } else {
                throw new ServiceException("返回数据格式错误，list字段无法识别");
            }
        } catch (Exception e) {
            log.error("解析list字段失败: {}", listObj, e);
            return "0";
        }

        if (list.isEmpty()) {
            return "0";
        }

        cn.hutool.json.JSONObject jsonObjectItem = list.getJSONObject(0);
        return jsonObjectItem.getStr("new_user");
    }

    /**
     * 获取js-sdk临时票据
     *
     * @param token 微信AccessToken
     * @return String 获取js-sdk临时票据
     */
    public String getJsapiTicket(String token) {
        //todo 缓存服务号全局token凭证
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/cgi-bin/ticket/getticket").append("?access_token=").append(token).append("&type=").append("jsapi");
        String rs = HttpUtil.get(url.toString());
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        if ("ok".equals(map.get("errmsg"))) {
            return map.get("ticket").toString();
        } else {
            log.error("获取js-sdk临时票据出错:{}", map);
            throw new ServiceException("获取js-sdk临时票据出错");
        }
    }

    /**
     * 刷新AccessToken
     *
     * @param refreshToken 用户刷新token
     * @return 返回包含刷新后的微信AccessToken的Map
     */
    public Map<?, ?> refreshToken(String refreshToken) {
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/sns/oauth2/refresh_token")
                .append("?appid=").append(wxPayProperties.getMpAppId())
                .append("&grant_type=refresh_token&refresh_token=").append(refreshToken);
        String rs = HttpUtil.get(url.toString());
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        if (null == map.get(ERROR_CODE)) {
            return map;
        } else {
            log.error("刷新access_token出错:{}", map);
            throw new ServiceException("刷新access_token出错");
        }
    }

    /**
     * 获取用户信息
     *
     * @param accessToken 微信AccessToken
     * @param openid      用户的openid
     * @return 返回包含用户信息的JSON字符串
     */
    public String getUserInfo(String accessToken, String openid) {
        StringBuffer url = new StringBuffer();
        url.append("https://api.weixin.qq.com/sns/userinfo")
                .append("?access_token=").append(accessToken)
                .append("&openid=").append(openid)
                .append("&lang=zh_CN");
        return HttpUtil.get(url.toString());
    }


    /**
     * 生成公众号码ticket
     *
     * @param token access_token
     * @param openId
     * @return Map<?, ?> 返回包含微信服务号ticket的Map
     */
    public Map<?, ?> getTicket(String token, String openId) {
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + token;
        String param = "{\"action_name\": \"QR_LIMIT_STR_SCENE\",\"action_info\": {\"scene\": {\"scene_str\": \"" + openId + "\"}}}";

        String rs = HttpUtil.post(url, param);
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
//        String ticket = map.get("ticket").toString();
//
//        String encode = null;
//        try {
//            encode = URLEncoder.encode(ticket, "UTF-8");
//        } catch (UnsupportedEncodingException e) {
//            throw new RuntimeException(e);
//        }
//
//        String url1 = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+ encode;
//
//        String s = HttpUtil.get(url1);
//
//
//
//
//        Map<?, ?> rsmap = JSONObject.parseObject(s, Map.class);
//        return rsmap;
        return map;
    }


    /**
     * 生成公众号码技师 ticket
     *
     * @param token access_token
     * @param jsId  技师id
     * @return 返回包含微信公众号码ticket的Map
     */
    public Map<?, ?> getJsTicket(String token, String jsId) {
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + token;
        String param = "{\"action_name\": \"QR_LIMIT_STR_SCENE\",\"action_info\": {\"scene\": {\"scene_str\": \"" + jsId + "\"}}}";
        String rs = HttpUtil.post(url, param);
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        return map;
    }


    /**
     * 生成公众号码ticket
     *
     * @param token access_token
     * @return 返回包含微信公众号码ticket的Map
     */
    public Map<?, ?> menuUtil(String token) {
        String url = " https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + token;

        //String param = "{\"button\":[{\"type\":\"view\",\"name\":\"立即下单\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/\"},{\"type\":\"view\",\"name\":\"查看商户\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/#/pages/identify/identify\"},{\"name\":\"更多\",\"sub_button\":[{\"type\":\"view\",\"name\":\"技师招募\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/#/pages/join/first_join\"},{\"type\":\"view\",\"name\":\"招商合作\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/#/pages/join/teamwork\"},{\"type\":\"view\",\"name\":\"投诉举报\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/#/pages/join/feedback\"},{\"type\":\"view\",\"name\":\"了解我们\",\"url\":\"https://city.baoxianzhanggui.com/fragrance/#/pages/join/understand\"}]}]}";
        String param = wxPayProperties.getMenu();
        String rs = HttpUtil.post(url, param);
        Map<?, ?> map = JSONObject.parseObject(rs, Map.class);
        return map;
    }

    /**
     * 服务号消息通知
     *
     * @param openid     用户的openid
     * @param templateId 模板id
     * @param data       消息数据
     * @return Map<?, ?> 返回包含微信公众号码ticket的Map
     */
    public Map<?, ?> notification(String openid, String templateId, cn.hutool.json.JSONObject data) {
        String token = this.getToken();
        log.info("token的值：{}", token);
        log.info("模版ID的值：{}", templateId);

        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + token;
        cn.hutool.json.JSONObject param1 = JSONUtil.createObj();
        param1.put("touser", openid);
        param1.put("template_id", templateId);
        param1.put("url", "https://test.baoxianzhanggui.com/fragrance/");
        param1.put("data", data);
        log.info("notification-消息通知请求参数:{}", param1.toString());

        String result = HttpUtil.post(url, param1.toString());
        Map<?, ?> map = JSONObject.parseObject(result, Map.class);
        log.info("notification-消息通知返回参数:{}", map);
        return map;
    }


    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            log.error("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
            throw ex;
        }
    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key : data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        } catch (Exception ex) {

        }
        return output;
    }


}
