package com.bohuikeji.delivery.backstage.util;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bohuikeji.delivery.backstage.constants.WxAppEm;
import com.bohuikeji.delivery.backstage.constants.WxTmplEm;
import com.bohuikeji.delivery.backstage.entity.wx.WeChatJsConfig;
import com.bohuikeji.frame.module.admin.component.RedisUtil;
import com.bohuikeji.frame.module.admin.utils.SpringUtil;
import com.bohuikeji.frame.module.admin.utils.StringUtils;
import jodd.http.HttpRequest;
import jodd.net.MimeTypes;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

/**
 * 微信业务工具类
 * @author 孙劲辉
 */
@Slf4j
public class WxUtils {

    private static final RedisUtil redisUtil= SpringUtil.getBean(RedisUtil.class);

    /**
     * 获取ACCESS_TOKEN地址
     * 微信公众号和小程序公用
     * 【参考文档】：https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
     * 【公众号】【小程序】
     */
    public static final String GET_ACCESS_TOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}";
    /**
     * 获取公众号的JS_API_TICKET地址
     * 【参考文档】：https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
     * 【公众号】
     */
    public static final String GET_GZH_JS_API_TICKET_URL="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={}&type=jsapi";

    /**
     * 获取小程序URL Link（分享链接）
     * 【参考文档】：https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-link/urllink.generate.html
     * 【小程序】
     */
    public static final String POST_MP_LINK_URL="https://api.weixin.qq.com/wxa/generate_urllink?access_token={}";

    /**
     * 使用code完成小程序登录，获取用户openid和session_key
     * 【参考文档】：https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     * 【小程序】
     */
    public static final String GET_MP_CODE_LOGIN= "https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code";

    /**
     * 微信公众号网页登录，使用code获取access_token
     * 【参考文档】：https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html#0
     * 【公众号网页开发】
     */
    public static final String GET_GZH_OAUTH2_ACCESS_TOKEN="https://api.weixin.qq.com/sns/oauth2/access_token?appid={}&secret={}&code={}&grant_type=authorization_code";

    /**
     * 微信公众号，获取用户信息
     * 【参考文档】：https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId
     * 【公众号】
     */
    public static final String GET_GZH_USER_INFO="https://api.weixin.qq.com/cgi-bin/user/info?access_token={}&openid={}&lang=zh_CN";

    /**
     * 微信公众号，发送模板消息接口
     * 【参考文档】：https://mp.weixin.qq.com/advanced/tmplmsg?action=faq&token=740651134&lang=zh_CN
     * 【公众号】
     */
    public static final String POST_GZH_SEND_TEMPLATE_MSG="https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}";

    /**
     *客户小程序生成二维码
     */
    public static final String GET_MP_CODE_CUSTOMER="https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token={}";

    /**
     * 获取小程序分享链接
     * @param path 小程序打开路径
     * @return 返回
     */
    public static JSONObject getCreateQRCode(WxAppEm app, String path){
        if (!"MP".equals(app.getType())){
            throw new RuntimeException("【获取小程序分享链接失败】APPID="+app.getAppId()+"不是小程序！");
        }
        JSONObject body=new JSONObject();
        if (StringUtils.isNotBlank(path)){
            body.put("path",path);
            //添加query参数
        }
        String resStr= HttpRequest.post(StrUtil.format(GET_MP_CODE_CUSTOMER,getAccessToken(app))).bodyText(body.toJSONString(), MediaType.APPLICATION_JSON_VALUE,"UTF-8").send().bodyText();
        JSONObject res=JSONObject.parseObject(resStr);
        if (res.getIntValue("errcode")!=0){
            if (res.getIntValue("errcode")==45029){
                throw new RuntimeException("【参数错误】页面路径：path="+path+res.getString("errmsg"));
            }
            if (res.getIntValue("errcode")==40159){
                throw new RuntimeException("【参数错误】页面路径：path="+path+res.getString("errmsg"));
            }
            throw new RuntimeException("获取小程序分享链接失败5："+res.getString("errmsg"));
        }
        return res;
    }


    /**
     * 获取小程序分享链接
     * @param path 小程序打开路径
     * @return 返回
     */
    public static JSONObject getMpCustomeCode(WxAppEm app, String path,String query){
        if (!"MP".equals(app.getType())){
            throw new RuntimeException("【获取小程序分享链接失败】APPID="+app.getAppId()+"不是小程序！");
        }
        JSONObject body=new JSONObject();
        if (StringUtils.isNotBlank(path)){
            body.put("path",path);
            //添加query参数
            if (StringUtils.isNotBlank(query)){
                body.put("query",query);
            }
        }
        String resStr= HttpRequest.post(StrUtil.format(POST_MP_LINK_URL,getAccessToken(app))).bodyText(body.toJSONString(), MediaType.APPLICATION_JSON_VALUE,"UTF-8").send().bodyText();
        JSONObject res=JSONObject.parseObject(resStr);
        if (res.getIntValue("errcode")!=0){
            if (res.getIntValue("errcode")==40165){
                throw new RuntimeException("【参数错误】页面路径：path="+path+"，不存在");
            }
            throw new RuntimeException("获取小程序分享链接失败5："+res.getString("errmsg"));
        }
        return res;
    }


    /**
     * 获取小程序分享链接
     * @param path 小程序打开路径
     * @return 返回
     */
    public static JSONObject getMpShareLink(WxAppEm app, String path,String query){
        if (!"MP".equals(app.getType())){
            throw new RuntimeException("【获取小程序分享链接失败】APPID="+app.getAppId()+"不是小程序！");
        }
        JSONObject body=new JSONObject();
        if (StringUtils.isNotBlank(path)){
            body.put("path",path);
            //添加query参数
            if (StringUtils.isNotBlank(query)){
                body.put("query",query);
            }
        }
        body.put("is_expire",true);
        body.put("expire_time", LocalDateTime.now().plusDays(30).toEpochSecond(ZoneOffset.of("+8")));
        String resStr= HttpRequest.post(StrUtil.format(POST_MP_LINK_URL,getAccessToken(app)))
                .bodyText(body.toJSONString(), MediaType.APPLICATION_JSON_VALUE,"UTF-8").send().bodyText();
        JSONObject res=JSONObject.parseObject(resStr);
        if (res.getIntValue("errcode")!=0){
            if (res.getIntValue("errcode")==40165){
                throw new RuntimeException("【参数错误】页面路径：path="+path+"，不存在");
            }
            log.error("【微信小程序接口错误】获取小程序分享链接失败：[{}]，错误码=[{}]",res.getString("errmsg"),res.getIntValue("errcode"));
            throw new RuntimeException("获取小程序分享链接失败："+res.getString("errmsg"));
        }
        return res;
    }

    /**
     * 获取token
     * 小程序和公众号公用
     * @return
     */
    public static String getAccessToken(WxAppEm app){
        String accessToken = (String) redisUtil.get("wx:cache:token:"+app.getAppId());
        if (StringUtil.isEmpty(accessToken)||accessToken.length()<64) {
            String result = HttpUtil.get(StrUtil.format(GET_ACCESS_TOKEN_URL, app.getAppId(),app.getAppSecret()));
            log.warn("WeChat:获取公众号accessToken结果: {}", result);
            JSONObject jsonObject = JSONObject.parseObject(result);
            accessToken = jsonObject.getString("access_token");
            if (StrUtil.isEmpty(accessToken)) {
                log.error("【公众号AccessToken获取错误】[{}]",result);
                return jsonObject.getString("errmsg");
                //throw new RuntimeException("【公众号AccessToken获取错误】"+jsonObject.getString("errmsg"));
            }
            // 保存redis(5分鐘)
            redisUtil.set("wx:cache:token:"+app.getAppId(), accessToken, 5*60-10/*jsonObject.getIntValue("expires_in")*/);
        }
        return accessToken;
    }

    /**
     * 获取获取公众号js-api-ticket
     * @return
     */
    public static String getJsApiTicket(WxAppEm app){
        String ticketStr = (String) redisUtil.get("js_sdk_ticket");
        if (StringUtil.isEmpty(ticketStr)) {
            String result = HttpUtil.get(StrUtil.format(GET_GZH_JS_API_TICKET_URL,getAccessToken(app)));
            log.warn("WeChat:获取公众号js-api-ticket结果: {}", result);
            JSONObject ticket = JSONObject.parseObject(result);
            ticketStr = ticket.getString("ticket");
            if (StrUtil.isEmpty(ticketStr)) {
                log.error("【公众号js-api-ticket获取错误】[{}]",result);
                return ticket.getString("errmsg");
            }
            // 保存redis
            redisUtil.set("js_sdk_ticket", ticketStr, ticket.getInteger("expires_in"));
        }
        return ticketStr;
    }

    /**
     * 获取微信公众号JS配置
     * 【公众号】
     * @param url
     * @return
     */
    public static WeChatJsConfig getJsConfig(WxAppEm app, String url){
        String nonceStr = DigestUtil.md5Hex(String.valueOf(new Random().nextInt(10000)));
        long timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
        String sign = "jsapi_ticket=" + getJsApiTicket(app) + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
        sign = DigestUtil.sha1Hex(sign);
        WeChatJsConfig weChatJsConfig = new WeChatJsConfig();
        weChatJsConfig.setAppId(app.getAppId());
        weChatJsConfig.setNonceStr(nonceStr);
        weChatJsConfig.setTimestamp(timestamp);
        weChatJsConfig.setSignature(sign);
        return weChatJsConfig;
    }

    /**
     * 小程序Code登录获取用户信息
     * @param code
     * @param app
     * @return
     */
    public static JSONObject getCodeLoginInfo(String code, WxAppEm app){
        String result = HttpUtil.get(StrUtil.format(GET_MP_CODE_LOGIN,app.getAppId(),app.getAppSecret(),code));
        JSONObject res= JSONObject.parseObject(result);
        if(res.getIntValue("errcode")!=0){
            log.error("【小程序CODE登录失败】：{}",res.toJSONString());
            throw new RuntimeException("【小程序CODE登录失败】："+res.toJSONString());
        }
        return res;
    }

    /**
     * code换取用户手机号
     * @param code
     * @param app
     * @return
     */
    public static JSONObject getLoginPhone(String code, WxAppEm app){
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={}";
        JSONObject body=new JSONObject();
        body.put("code",code);
        String resStr= HttpRequest.post(StrUtil.format(url,getAccessToken(app)))
                .bodyText(body.toJSONString(), MediaType.APPLICATION_JSON_VALUE,"UTF-8").send().bodyText();
        log.info("查询微信获取手机号接口返回：[{}]",resStr);
        JSONObject res= JSON.parseObject(resStr);
        if(res.getIntValue("errcode")!=0){
            log.error("【小程序CODE换取手机号失败】：{}",res.toJSONString());
            throw new RuntimeException("【小程序CODE换取手机号失败】："+res.toJSONString());
        }
        return res.getJSONObject("phone_info");
    }

    /**
     * 获取公众号网页开发token
     * @param app
     * @param code 微信回调的code
     * @return
     */
    public static JSONObject getGzhOauthToken(WxAppEm app, String code){
        String result = HttpUtil.get(StrUtil.format(GET_GZH_OAUTH2_ACCESS_TOKEN, app.getAppId(),app.getAppSecret(),code));
        JSONObject jsonObject = JSONObject.parseObject(result);
        String accessToken = jsonObject.getString("access_token");
        if (StrUtil.isEmpty(accessToken)) {
            log.error("【公众号OauthAccessToken获取错误】[{}]",result);
            throw new RuntimeException("【公众号OauthAccessToken获取错误】"+ result);
        }
        return jsonObject;
    }

    /**
     * 获取公众号用户信息
     * @param app 公众号信息
     * @param openid 用户openid
     * @return
     */
    public static JSONObject getGzhUserInfo(WxAppEm app, String openid){
        String result = HttpUtil.get(StrUtil.format(GET_GZH_USER_INFO, getAccessToken(app),openid));
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject.getInteger("errcode")!=null){
            log.error("【公众号获取用户信息错误】[{}]",result);
            throw new RuntimeException("【公众号获取用户信息错误】"+ result);
        }
        return jsonObject;
    }

    /**
     * 判断用户是否关注了公众号
     * @param app
     * @param openid
     * @return
     */
    public static boolean isFollowGzh(WxAppEm app, String openid){
        JSONObject res = getGzhUserInfo(app,openid);
        return res.getIntValue("subscribe") == 1;
    }

    /**
     * 快捷发送模板消息
     *@param openId 接收人openid
     * @param tmpl 消息模板
     * @param params 参数
     * @return 返回消息体
     */
    public static JSONObject sendTmplMsg(WxAppEm app,WxTmplEm tmpl, String openId, Map<String,Object> params){
        JSONObject msg=new JSONObject();
        msg.put("touser",openId);
        msg.put("template_id",tmpl.getTemplateId());
        msg.put("topcolor",tmpl.getTopColor());
        msg.put("ulr",tmpl.getUrl());
        if(StringUtils.isNotBlank(tmpl.getMpAppid())){
            JSONObject miniprogram=new JSONObject();
            miniprogram.put("appid",tmpl.getMpAppid());
            miniprogram.put("pagepath",tmpl.getMpPagePath());
            msg.put("miniprogram",miniprogram);
        }
        JSONObject data=new JSONObject();
        params.forEach((k,v)->{
            JSONObject value=new JSONObject();
            value.put("value",v);
            value.put("color",tmpl.getParamColor());
            data.put(k,value);
        });
        msg.put("data",data);
        String result = HttpUtil.post(StrUtil.format(POST_GZH_SEND_TEMPLATE_MSG,getAccessToken(app)),msg.toJSONString());
        JSONObject jsonObject = JSONObject.parseObject(result);
        log.info("##################################3:{}",jsonObject);
        if (jsonObject.getInteger("errcode")!=0){
            log.error("【发送模板消息失败】模板名称：{}，错误信息{}",tmpl.getName(),result);
        }
        return msg;
    }
}
