package com.nuliji.tools.third.wechat;

import com.alibaba.fastjson.JSONObject;
import com.nuliji.tools.Tools;
import com.nuliji.tools.third.OauthData;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.URLEncoder;
import java.util.*;

public class WechatClient {
    private static final Logger logger = LoggerFactory.getLogger(WechatClient.class);

    private String appId = "";
    private String appSecret = "";

    public WechatClient(String appId, String appSecret){
        this.appId = appId;
        this.appSecret = appSecret;
    }


    private static Map<String, String> tokenMap = new HashMap<>();
    private static Map<String, Date> tokenExpiresIn = new HashMap<>();
    private static Map<String, String> jsTicketMap = new HashMap<>();
    private static Map<String, Date> jsTicketExpiresIn = new HashMap<>();
    /**
     * 从微信服务器获取临时访问二维码
     *
     */
    public String getQrcode(int sceneId) {
        String accessToken = getAccessToken(appId, appSecret);
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken;
        Map<String, Object> body = new HashMap<>();
        body.put("expire_seconds", 3600);
        body.put("action_name", "QR_SCENE");
        Map<String, Object> actionInfo = new HashMap<>();
        Map<String, Object> scene = new HashMap<>();
        scene.put("scene_id", sceneId);
        actionInfo.put("scene", scene);
        body.put("action_info", actionInfo);

        JSONObject object = execute(url, null, body);

        return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + object.getString("ticket");
    }

    public String getOAuthUrl(String redirectUrl ,String state){
        return "https://open.weixin.qq.com/connect/qrconnect?appid="+appId+"&redirect_uri="+redirectUrl+"&response_type=code&scope=snsapi_login&state="+state+"#wechat_redirect";
    }

    /**
     * 取微信公众平台accesstoken
     *
     * @return
     */
    private synchronized String getAccessToken(String appid, String secret) {
        Date date = new Date();
        // 系统刚启动时，或者离上次取accessToken有7180秒以上时，重新取它
        if (!tokenMap.containsKey(appid) || date.getTime() / 1000 - tokenExpiresIn.get(appid).getTime() / 1000 >= 7180) {
            Map<String, String> params = new HashMap<>();
            params.put("grant_type", "client_credential");
            params.put("appid", appid);
            params.put("secret", secret);
            JSONObject object = execute("https://api.weixin.qq.com/cgi-bin/token", params, null);
            tokenMap.put(appid, object.getString("accessToken"));
            tokenExpiresIn.put(appid, date);
            logger.info("getAccessToken:" + tokenMap.get(appid));
        }

        return tokenMap.get(appid);
    }

    /**
     * 取微信公众平台js_ticket
     *
     * @return
     */
    private synchronized String getJSTicket(String appid, String secret) {
        String accessToken = getAccessToken(appid, secret);
        Date date = new Date();
        // 系统刚启动时，或者离上次取accessToken有7180秒以上时，重新取它
        if (!jsTicketMap.containsKey(accessToken) || date.getTime() / 1000 - jsTicketExpiresIn.get(accessToken).getTime() / 1000 >= 7180) {
            Map<String, String> params = new HashMap<>();
            params.put("accessToken", accessToken);
            params.put("type", "jsapi");
            JSONObject object = execute("https://api.weixin.qq.com/cgi-bin/ticket/getticket", params, null);
            jsTicketMap.put(accessToken, object.getString("ticket"));
            jsTicketExpiresIn.put(accessToken, date);
            logger.info("getJSTicket:" + jsTicketMap.get(accessToken));
        }

        return jsTicketMap.get(accessToken);
    }

    private JSONObject getWebAccessToken(String code) {
        Map<String, String> params = new HashMap<>();
        params.put("appid", appId);
        params.put("secret", appSecret);
        params.put("code", code);
        params.put("grant_type", "authorization_code");
        JSONObject object = execute("https://api.weixin.qq.com/sns/oauth2/access_token", params, null);

        return object;
    }


    public OauthData webAuthorize(String code) {
        JSONObject result = getWebAccessToken(code);

        // 从result中解析出openid和session
        if (result.get("errcode") != null) {
            logger.warn(String.format("jscodeToSession failed: result = %s", result));
            throw new RuntimeException(String.format("从微信服务器换取用户登录态信息: 错误码: %d, 错误信息: %s", result.getIntValue("errcode"), result.getString("errmsg")));
        }
        JSONObject info = getWebUserInfo(result.getString("access_token"), result.getString("openid"));
        OauthData data = new OauthData();
        data.setRefreshToken(result.getString("refresh_token"));
        data.setAccessToken(result.getString("access_token"));
        data.setExpiresTime(new Date(new Date().getTime() + result.getInteger("expires_in")));
        data.setAvatar(info.getString("avatar"));
        data.setNickName(info.getString("nickname"));
        data.setUnionId(info.getString("unionid"));
        data.setOpenId(info.getString("openid"));
        data.setGender(info.getIntValue("sex") == 1 ? "mela":"femela");
        return data;
    }

    public JSONObject getWebUserInfo(String accessToken, String openid) {
        Map<String, String> params = new HashMap<>();
        params.put("access_token", accessToken);
        params.put("openid", openid);
        params.put("lang", "zh_CN");
        JSONObject object = execute("https://api.weixin.qq.com/sns/userinfo", params, null);
        return object;
    }

    public OauthData refreshWebAccessToken(String refreshToken){
        Map<String, String> params = new HashMap<>();
        params.put("appid", appId);
        params.put("refresh_token", refreshToken);
        params.put("grant_type", "refresh_token");
        JSONObject result = execute("https://api.weixin.qq.com/sns/oauth2/refresh_token", params, null);
        OauthData data = new OauthData();
        data.setRefreshToken(result.getString("refresh_token"));
        data.setAccessToken(result.getString("access_token"));
        data.setExpiresTime(new Date(new Date().getTime() + result.getInteger("expires_in")));

        return data;
    }

    /**
     * 处理接收到的微信消息
     *
     * @param request
     * @param listener
     * @return
     */
    public void ReceiveMessage(String request, MessageListener listener) {
        logger.info("receive -------------------------------------- = " + request);
        TreeMap<String, String> map = Tools.parseXml(request);
        if (map == null) {
            logger.warn("processReceiveWeixinMessage(): request is null");
            return ;
        }

        String openId = map.get("FromUserName");
        if (openId == null) {
            logger.error("processReceiveWeixinMessage(): FromUserName(openid) error: " + request);
            return;
        }

        // 已关注
        // 未关注进行关注
        if ("event".equals(map.get("MsgType")) && "SCAN".equals(map.get("Event"))) {

            // 后台根据场景值A，查询到对应的用户ID
            int sceneId = Integer.valueOf(map.get("EventKey"));
            listener.OnScan(openId, sceneId);
        } else if ("event".equals(map.get("MsgType")) && "subscribe".equals(map.get("Event"))) {

            // 关注事件-普通关注事件
            if (StringUtils.isBlank(map.get("EventKey"))) {
                listener.OnSubscribe(openId);
            }
            else if (map.get("EventKey").startsWith("qrscene_")) // 关注事件-扫描带参数二维码事件(内部帐号绑定微信号)
            {
                // 后台根据场景值A，查询到对应的用户ID
                int sceneId = Integer.valueOf(map.get("EventKey").substring("qrscene_".length()));

                listener.OnSubscribe(openId);
                listener.OnScan(openId, sceneId);
            }
        }
    }

    /**
     * 公众号发送微信消息给指定商家
     *
     * @param openid
     * @param templateId
     * @param dataUrl
     * @param dataMap
     * @return 返回数据，正常返回的例子： {"errcode":0,"errmsg":"ok","msgid":200228332}
     */
    private JSONObject sendMessage(String openid, String templateId, String dataUrl, HashMap<String, String> dataMap) {
        String accessToken = getAccessToken(appId, appSecret);
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
        Map<String, String> params = new HashMap<>();
        params.put("access_token", accessToken);

        Map<String, Object> body = new HashMap<>();
        body.put("touser", openid);
        body.put("template_id", templateId);
        if (dataUrl != null){
            body.put("url", dataUrl);
        }
        body.put("data", null);
        JSONObject object = execute(url, params, body);
        return object;
    }

    private JSONObject sendCustomMessage(String openid, String content) {
        String accessToken = getAccessToken(appId, appSecret);
        String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send";
        Map<String, String> params = new HashMap<>();
        params.put("access_token", accessToken);
        HttpHeaders headers = new HttpHeaders();

        Map<String, Object> body = new HashMap<>();
        body.put("touser", openid);
        body.put("msgtype", "text");
        Map<String, Object> text = new HashMap<>();
        text.put("content", content);
        body.put("text", text);
        body.put("data", null);
        JSONObject object = execute(url, params, body);
        return object;
    }

    private JSONObject execute(String api, Map<String, String> urlParams, Map<String,Object> bodyParams) throws RuntimeException {
        try {
            String paramStr = "";
            if (urlParams != null){
                List<String> keys = new ArrayList<>(urlParams.keySet());
                Collections.sort(keys);
                for (int i = 0; i < keys.size(); i++) {
                    String key = keys.get(i);
                    String value = urlParams.get(key);
                    value = URLEncoder.encode(value, "UTF-8");
                    if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
                        paramStr = paramStr + key + "=" + value;
                    } else {
                        paramStr = paramStr + key + "=" + value + "&";
                    }
                }
            }

            HttpHeaders headers = new HttpHeaders();
            MediaType type = MediaType.APPLICATION_JSON;
            headers.setContentType(type);
            RestTemplate restTemplate = new RestTemplate();
            String result ="";
            if (bodyParams == null){
                result = restTemplate.getForObject(api+"?"+paramStr, String.class);
            } else {
                HttpEntity<String> formEntity = new HttpEntity<>(JSONObject.toJSONString(bodyParams), headers);
                result = restTemplate.postForObject(api+ "?" + paramStr, formEntity, String.class);
            }
            logger.info("微信返回结果:" + result);
            JSONObject a = JSONObject.parseObject(result);

            if(a.getInteger("status") == 0){
                throw new Exception("接口错误"+a.getString("info"));
            }
            return a;

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
