package com.ruoyi.common.weixin.utils;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.weixin.config.WeiXinGzhConfig;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * 微信公众号工具类
 */
@Component
public class WeiXinGzhUtils {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RedisCache redisCache;

    @Resource
    private WeiXinGzhConfig weiXinGzhConfig;

    /**
     * 获取access_token
     *
     * @return access_token
     */
    public String getAccessToken() {
        JSONObject jsonObject = new JSONObject();
        String accessToken = "";
        try {
            if (redisCache.hasKey(CacheConstants.GZH_ACCESS_TOKEN_KEY)) {
                return redisCache.getCacheObject(CacheConstants.GZH_ACCESS_TOKEN_KEY).toString();
            }
            String requestUrl = "https://api.weixin.qq.com/cgi-bin/token" +
                    "?grant_type=client_credential" +
                    "&appid="+weiXinGzhConfig.getAppId() +
                    "&secret="+weiXinGzhConfig.getAppSecret();
            String result = HttpUtil.get(requestUrl);
            logger.info("{} 公众号获取 access_token 结果 -> {}", DateUtils.getTime(), result);
            jsonObject = JSONObject.parseObject(result);
            // 有效期(s)
            Long expiresIn = 0L;
            if (ObjectUtils.isNotEmpty(jsonObject)) {
                accessToken = jsonObject.getString("access_token");
                expiresIn = jsonObject.getLong("expires_in");
            }
            if (StringUtils.isBlank(accessToken)){
                logger.error("{} 公众号获取 access_token 失败 -> {}", DateUtils.getTime(), result);
            }else {
                redisCache.setCacheObject(CacheConstants.GZH_ACCESS_TOKEN_KEY, accessToken, expiresIn.intValue(), SECONDS);
            }
        } catch (Exception e) {
            logger.error("{} 公众号获取 access_token 异常 -> {}", DateUtils.getTime(), e.getMessage());
        }
        return accessToken;
    }

    /**
     * 获取ticket
     * @return 结果
     */
    public JSONObject getQrCodeByTicket(){
        String ticket = "";
        String uuid = IdUtils.simpleUUID();
        String qrUrl = "";

        String accessToken = this.getAccessToken();
        if (StringUtils.isBlank(accessToken)){
            throw new ServiceException("微信校验失败");
        }
        try {
            // 通过access_token和一些参数发送post请求获取二维码Ticket
            String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
            // 封装参数
            JSONObject ticketInfo = new JSONObject();
            ticketInfo.put("expire_seconds", 300); // 二维码超时时间
            ticketInfo.put("action_name", "QR_STR_SCENE");
            // 放入uuid
            ticketInfo.put("action_info", new JSONObject() {{
                        put("scene", new JSONObject() {{
                                    put("scene_str", uuid);
                                }}
                        );
                    }}
            );
            String ticketJsonInfo = ticketInfo.toJSONString();// 请求参数
            String ticketRes = HttpUtil.post(getTicketUrl, ticketJsonInfo);
            logger.info("{} 公众号获取 ticket 结果 -> {}", DateUtils.getTime(), ticketRes);
            JSONObject ticketResult = JSONObject.parseObject(ticketRes);
            if (ticketResult.containsKey("ticket")){
                ticket = ticketResult.getString("ticket");
            }else {
                logger.error("{} 公众号获取 ticket 失败 -> {}", DateUtils.getTime(), ticketRes);
            }
        }catch (Exception e){
            logger.error("{} 公众号获取 ticket 异常 -> {}", DateUtils.getTime(), e.getMessage());
        }
        if (StringUtils.isBlank(ticket)){
            throw new ServiceException("微信授权异常");
        }

        try {
            // 通过ticket获取二维码url
            String encodeTicket = URLEncoder.encode(ticket, "utf-8"); // 编码ticket
            qrUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + encodeTicket; // 二维码url
        }catch (Exception e){
            logger.error("{} 公众号获取 qrCode 异常 -> {}", DateUtils.getTime(), e.getMessage());
        }

        if (StringUtils.isBlank(ticket)){
            throw new ServiceException("微信授权失败");
        }

        if (!isValidURL(qrUrl)){
            throw new ServiceException("登录");
        }

        JSONObject result = new JSONObject();

//        result.put("ticket", ticket);
        result.put("uuid", uuid);
        result.put("qrUrl", qrUrl);
        return result;
    }

    /**
     * 检测二维码链接是否有效
     * @param urlString 链接
     * @return 结果 boolean
     */
    public static boolean isValidURL(String urlString) {
        if (urlString == null || !urlString.startsWith("http://") && !urlString.startsWith("https://")) {
            return false;
        }

        try {
            URL url = new URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD");
            connection.setConnectTimeout(5000); // 设置连接超时时间（毫秒）
            connection.setReadTimeout(5000); // 设置读取超时时间（毫秒）
            int responseCode = connection.getResponseCode();
            return 200 == responseCode; // 检查HTTP响应码是否在200到399之间
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 校验微信服务器Token签名
     *
     * @param signature 微信加密签名
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @return boolean
     */
    public boolean checkSignature(String signature, String timestamp, String nonce) {
        String checktext = null;
        if (null != signature) {
            //对ToKen,timestamp,nonce 按字典排序
            String[] paramArr = new String[]{weiXinGzhConfig.getToken(), timestamp, nonce};
            Arrays.sort(paramArr);
            //将排序后的结果拼成一个字符串
            String content = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                //对接后的字符串进行sha1加密
                byte[] digest = md.digest(content.getBytes());
                checktext = byteToStr(digest);
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        }
        //将加密后的字符串与signature进行对比
        return checktext != null && checktext.equals(signature.toUpperCase());
    }

    /**
     * 将字节数组转化我16进制字符串
     *
     * @param byteArrays 字符数组
     * @return 字符串
     */
    private String byteToStr(byte[] byteArrays) {
        String str = "";
        for (int i = 0; i < byteArrays.length; i++) {
            str += byteToHexStr(byteArrays[i]);
        }
        return str;
    }

    /**
     * 将字节转化为十六进制字符串
     *
     * @param myByte 字节
     * @return 字符串
     */
    private String byteToHexStr(byte myByte) {
        char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        char[] tampArr = new char[2];
        tampArr[0] = Digit[(myByte >>> 4) & 0X0F];
        tampArr[1] = Digit[myByte & 0X0F];
        String str = new String(tampArr);
        return str;
    }

    public Map<String,String> xmlToMap(String strxml) throws JDOMException, IOException {

        if(StringUtils.isBlank(strxml)) {
            return null;
        }

        Map<String, String> m = new HashMap<String, String>();

        InputStream in = new ByteArrayInputStream(strxml.getBytes(StandardCharsets.UTF_8));
        SAXBuilder builder = new SAXBuilder();
        Document doc = builder.build(in);
        Element root = doc.getRootElement();
        List<Element> list = root.getChildren();
        for (Element e : list) {
            String k = e.getName();
            String v = "";
            List<Element> children = e.getChildren();
            if (children.isEmpty()) {
                v = e.getTextNormalize();
            } else {
                v = getChildrenText(children);
            }

            m.put(k, v);
        }

        //关闭流
        in.close();

        return m;
    }

    public static String getChildrenText(List<Element> children) {
        StringBuilder sb = new StringBuilder();
        if(!children.isEmpty()) {
            for (Element e : children) {
                String name = e.getName();
                String value = e.getTextNormalize();
                List<Element> 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();
    }

}
