package com.yida.data.common.core.utils;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yida.data.common.core.entity.GenerateUrlLink;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.entity.constant.QywxConstant;
import com.yida.data.common.core.entity.constant.WxPublicConstant;
import com.yida.data.common.core.entity.notice.qywx.inside.News;
import com.yida.data.common.core.exception.FebsException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Component
@RequiredArgsConstructor
public class WxPublicUtil {

    public final static String DEVICE_OFFLINE_TEMPLATE_ID = "YBUVTKRXXsguLHH-Gw7uZ-zXBJ1bU1-HhndlQKhJsTg";
    private final RedisService redisService;

    private static String post(String url, String body) {
        String res = null;
        try {
            res = HttpUtil.post(url, body);
        } catch (Exception e) {
            if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("UnknownHostException")) {
                res = post(url, body);
            }
        }
        return res;
    }

    private static String post(String url, Map body) {
        String res = null;
        try {
            res = HttpUtil.post(url, body);
        } catch (Exception e) {
            if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("UnknownHostException")) {
                res = post(url, body);
            }
        }
        return res;
    }

    private static String post(Integer count, String url, String body) {
        if (count <= 3) {
            String res = null;
            try {
                res = HttpUtil.post(url, body);
            } catch (Exception e) {
                if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("UnknownHostException")) {
                    res = post(url, body);
                }
            }
            return res;
        } else {
            return null;
        }
    }

    private static String post(Integer count, String url, Map body) {
        if (count <= 3) {
            String res = null;
            try {
                res = HttpUtil.post(url, body);
            } catch (Exception e) {
                if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("UnknownHostException")) {
                    res = post(url, body);
                }
            }
            return res;
        } else {
            return null;
        }
    }

    private static String get(String url, Map params) {
        return get(1, url, params);
    }

    private static String get(Integer count, String url, Map params) {
        if (count <= 3) {
            String res = null;
            try {
                res = HttpUtil.get(url, params);
            } catch (Exception e) {
                if (StrUtil.isNotBlank(e.getMessage()) && e.getMessage().contains("UnknownHostException")) {
                    res = get(url, params);
                }
            }
            return res;
        } else {
            return null;
        }
    }

    public String getAccessToken(String appId, String secret) {
        String accessToken =
            String.valueOf(redisService.get(FebsConstant.WX_PUBLIC_ACCESS_TOKEN + appId + "." + secret));
        if ("null".equals(accessToken)) {
            Map<String, Object> params = new HashMap<>();
            params.put("grant_type", "client_credential");
            params.put("appid", appId);
            params.put("secret", secret);
            // 请求微信access token
            String response = get(WxPublicConstant.GET_ACCESS_TOKEN, params);
            JSONObject token = JSONUtil.parseObj(response);
            if (token.containsKey("errcode") && !"0".equals(token.get("errcode").toString())) {
                log.error("请求微信公众号access token失败，code:{},msg:{}", token.get("errcode").toString(),
                    token.get("errmsg").toString());
                throw new FebsException("验证失败");
            }
            accessToken = token.get("access_token").toString();
            // 缓存accessToken两小时
            redisService.set(FebsConstant.WX_PUBLIC_ACCESS_TOKEN + appId + "." + secret, accessToken, 7200L);
        }
        return accessToken;
    }

    public String getOpenId(String appId, String secret, String code) {
        Map<String, Object> params = new HashMap<>();
        params.put("appId", appId);
        params.put("secret", secret);
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        // 请求微信access token
        String response = get(QywxConstant.GET_PUBLIC_TOKEN_URL, params);
        JSONObject token = JSONUtil.parseObj(response);
        if (token.containsKey("errcode") && !"0".equals(token.get("errcode"))) {
            log.error("请微信求access token失败，code:{},msg:{}", token.get("errcode"),
                token.get("errmsg"));
            throw new FebsException("验证失败");
        }
        return token.getStr("openid");
    }

    /**
     * 上传图片
     */
    public String uploadNewsImg(File img, String token) {
        Map<String, Object> body = new HashMap<>();
        body.put("media", img);
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.UPLOAD_NEWS_IMG + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("上传图片失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("上传图片失败");
        }
        return response.getStr("url");
    }

    /**
     * 上传图文素材
     */
    public String uploadNews(News news, String token) {
        String body = JSONUtil.toJsonPrettyStr(news);
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.UPLOAD_NEWS + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("上传图文素材失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("上传图文素材失败");
        }
        return response.getStr("media_id");
    }

    /**
     * 群发图文消息
     */
    public String sendNews(String mediaId, String token) {
        Map<String, Object> body = new HashMap<String, Object>() {{
            put("filter", new HashMap<String, Object>() {{
                put("is_to_all", true);
            }});
            put("mpnews", new HashMap<String, Object>() {{
                put("media_id", mediaId);
            }});
            put("msgtype", "mpnews");
            put("send_ignore_reprint", 1);
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.SEND_MSG + "?access_token=" + token, JSONUtil.toJsonStr(body)));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("群发图文消息失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("群发图文消息失败");
        }
        return response.getStr("msg_data_id");
    }

    /**
     * 群发文本消息
     */
    public String sendText(String content, String token) {
        JSONObject body = new JSONObject() {{
            set("filter", new JSONObject() {{
                set("is_to_all", true);
            }});
            set("text", new JSONObject() {{
                set("content", content);
            }});
            set("msgtype", "text");
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.SEND_MSG + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("群发文本消息失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("群发文本消息失败");
        }
        return response.getStr("msg_data_id");
    }

    /**
     * 群发音频消息
     */
    public String sendVoice(String mediaId, String token) {
        JSONObject body = new JSONObject() {{
            set("filter", new JSONObject() {{
                set("is_to_all", true);
            }});
            set("voice", new JSONObject() {{
                set("media_id", mediaId);
            }});
            set("msgtype", "voice");
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.SEND_MSG + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("群发音频消息失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("群发音频消息失败");
        }
        return response.getStr("msg_data_id");
    }

    /**
     * 群发图片消息
     */
    public String sendImage(String[] mediaIds, String token) {
        JSONObject body = new JSONObject() {{
            set("filter", new JSONObject() {{
                set("is_to_all", true);
            }});
            set("images", new JSONObject() {{
                set("media_ids", mediaIds);
            }});
            set("msgtype", "image");
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.SEND_MSG + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("群发音频消息失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("群发音频消息失败");
        }
        return response.getStr("msg_data_id");
    }

    /**
     * 群发视频消息
     */
    public String sendVideo(String mediaId, String token) {
        JSONObject body = new JSONObject() {{
            set("filter", new JSONObject() {{
                set("is_to_all", true);
            }});
            set("mpvideo", new JSONObject() {{
                set("media_ids", mediaId);
            }});
            set("msgtype", "mpvideo");
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.SEND_MSG + "?access_token=" + token, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("群发音频消息失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("群发音频消息失败");
        }
        return response.getStr("msg_data_id");
    }

    /**
     * 上传永久素材   图片（image）、语音（voice）、视频（video）和缩略图（thumb）
     */
    public String uploadMaterial(String type, String videoTitle, String videoDescrip, File file, String token) {
        Map body = new HashMap<String, Object>() {{
            put("media", file);
        }};
        if ("video".equals(type)) {
            body.put("description", JSONUtil.toJsonStr(new HashMap<String, Object>() {{
                put("title", videoTitle);
                put("introduction", videoDescrip);
            }}));
        }
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.UPLOAD_MATERIAL + "?access_token=" + token + "&type=" + type, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("上传永久素材失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("上传永久素材失败");
        }
        return response.getStr("media_id");
    }

    /**
     * 删除永久素材
     */
    public void deleteMaterial(String mediaId, String token) {
        Map body = new HashMap() {{
            put("media_id", mediaId);
        }};
        JSONObject response = JSONUtil.parseObj(
            post(WxPublicConstant.DELETE_MATERIAL + "?access_token=" + token, JSONUtil.toJsonStr(body)));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("删除永久素材失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("删除永久素材失败");
        }
    }

    /**
     * 上传临时素材   图片（image）、语音（voice）、视频（video）和缩略图（thumb）
     */
    public String uploadTempMaterial(String type, File file,
        String token) {
        Map<String, Object> body = new HashMap<String, Object>() {{
            put("media", file);
        }};
        JSONObject response = JSONUtil.parseObj(
            HttpUtil.post(WxPublicConstant.UPLOAD_TEMP_MATERIAL + "?access_token=" + token + "&type=" + type, body));
        if (response.containsKey("errcode") && !"0".equals(response.get("errcode").toString())) {
            log.error("上传临时素材失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("上传临时素材失败");
        }
        if ("thumb".equals(type)) {
            return response.getStr("thumb_media_id");
        }
        return response.getStr("media_id");
    }

    /**
     * 生成关注二维码
     *
     * @param accessToken token
     * @param sceneStr    场景参数(总长度不超过64)  全局处理,格式为:类型标识+唯一标识(使用'-'连接)
     * @param expire      过期时间(单位：秒)  默认60秒  最长2592000(30天)
     * @return
     */
    public String generateQR(String accessToken, String sceneStr, Integer expire) {
        if (sceneStr.length() > 64) {
            return null;
        }
        String url = new StringBuilder(WxPublicConstant.QR_CODE).append("?access_token=").append(accessToken)
            .toString();
        JSONObject body = new JSONObject() {{
            if (expire != null) {
                set("expire_seconds", expire);
            }
            set("action_name", "QR_STR_SCENE");
            set("action_info", new JSONObject() {{
                set("scene", new JSONObject() {{
                    set("scene_str", sceneStr);
                }});
            }});
        }};
        String post = HttpUtil.post(url, body.toString());
        log.info("获取二维码:[{}]", post);
        JSONObject jsonObject = JSONUtil.parseObj(post);
        return jsonObject.getStr("ticket");
    }

    /**
     * 获取用户信息
     *
     * @param accessToken
     * @param openId
     * @return
     */
    public JSONObject getUserInfo(String accessToken, String openId) {
        JSONObject params = new JSONObject() {{
            set("access_token", accessToken);
            set("lang", "zh_CN");
            set("openid", openId);
        }};
        return JSONUtil.parseObj(get(WxPublicConstant.USER_INFO, params));
    }


    public void pushCustomMsgText(String accessToken, String openUserId, String msg) {
        JSONObject body = new JSONObject() {{
            set("touser", openUserId);
            set("msgtype", "text");
            set("text", new JSONObject() {{
                set("content", msg);
            }});
        }};
        String post = HttpUtil.post(
            new StringBuilder(WxPublicConstant.PUSH_CUSTOM_MSG).append("?access_token=").append(accessToken).toString(),
            body.toString());
        log.info("发送客户消息,res:[{}],body:[{}]", post, body);
    }

    public void publishTemplateMsg(String accessToken, String openUserId, String templateId, String url,
        JSONObject data) {
        JSONObject body = new JSONObject() {{
            set("touser", openUserId);
            if (StrUtil.isNotBlank(url)) {
                set("url", url);
            }
            set("template_id", templateId);
            set("data", data);
        }};
        String post = HttpUtil.post(new StringBuilder(WxPublicConstant.PUSH_TEMPLATE_MSG).append(
            "?access_token=").append(accessToken).toString(), body.toString());
        log.info("发送模板消息,res:[{}],body:[{}]", post, body);
    }

    /**
     * 获取小程序URL Link https + post
     *
     * @param accessToken     token值
     * @param generateUrlLink 获取小程序URL Link请求类
     * @return java.lang.String
     * @author ZYJ
     * @date 2022/6/21 17:17
     */
    public String generateUrlLink(String accessToken, GenerateUrlLink generateUrlLink) {
        // 封装参数
        JSONObject body = JSONUtil.createObj()
            .set("path", generateUrlLink.getPath())
            .set("query", generateUrlLink.getQuery())
            .set("env_version", generateUrlLink.getEnvVersion())
            .set("expire_type", generateUrlLink.getExpireType())
            .set("expire_interval", 30);
        if (Objects.nonNull(generateUrlLink.getCloudBase())) {
            GenerateUrlLink.CloudBase cloudBase = generateUrlLink.getCloudBase();
            body.set("cloud_base", JSONUtil.createObj()
                .set("env", cloudBase.getEnv())
                .set("domain", cloudBase.getDomain())
                .set("path", cloudBase.getPath())
                .set("query", cloudBase.getQuery())
                .set("resource_appid", cloudBase.getResourceAppId())
            );
        }
        // 请求接口
        String result = HttpUtil.post(WxPublicConstant.GENERATE_URL_LINK + "?access_token=" + accessToken, body.toString());
//        log.info("获取小程序URL Link接口, result: [{}], body: [{}]", result, body);
        JSONObject response = JSONUtil.parseObj(result);
        if (response.containsKey("errcode") && !"0".equals(response.getStr("errcode"))) {
            log.error("获取小程序URL Link接口失败，code:{},msg:{}", response.get("errcode"),
                response.get("errmsg"));
            throw new FebsException("获取小程序URL Link接口失败");
        }
        return response.getStr("url_link");
    }

    /**
     * 微信获取JS-SDK jsapi_ticket
     *
     * @param appId  微信appId
     * @param secret 微信secret
     * @return java.lang.String
     * @author ZYJ
     * @date 2023/6/5 14:50
     */
    public String getJsApiTicket(String appId, String secret) {
        String jsApiTicket =
            String.valueOf(redisService.get(FebsConstant.WX_PUBLIC_JS_API_TICKET + appId + "." + secret));
        if ("null".equals(jsApiTicket)) {

            Map<String, Object> params = new HashMap<>();
            params.put("access_token", getAccessToken(appId, secret));
            params.put("type", "jsapi");
            // 获取JS-SDK jsapi_ticket
            String response = get(WxPublicConstant.GET_JS_API_TICKET_URL, params);
            JSONObject token = JSONUtil.parseObj(response);
            if (token.containsKey("errcode") && !"0".equals(token.get("errcode").toString())) {
                log.error("获取JS-SDK jsapi_ticket失败，code:{},msg:{}", token.get("errcode").toString(),
                    token.get("errmsg").toString());
                throw new FebsException("验证失败");
            }
            jsApiTicket = token.get("ticket").toString();
            // 缓存accessToken两小时
            redisService.set(FebsConstant.WX_PUBLIC_JS_API_TICKET + appId + "." + secret, jsApiTicket, 7200L);
        }
        return jsApiTicket;
    }
}
