package com.platform.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.platform.entity.AccessTokenModel;
import com.platform.entity.SubscribeMessageVo;
import com.platform.entity.WxLiveRoomRequestVo;
import com.platform.entity.WxMsgVo;
import com.platform.enums.ResultCodeEnum;
import com.platform.exception.CommonBaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.*;

/**
 * @author abnerhou
 * @date 2020/4/30 8:49
 * @desciption 微信登录操作工具类
 */
@Slf4j
@Component
public class WxOpUtils {

    private static RestTemplate restTemplate;

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate){
        WxOpUtils.restTemplate = restTemplate;
    }

    public static final String SESSION_KEY = "session_key";
    public static final String WX_OPEN_ID_KEY = "openid";
    public static final String WX_ACCESS_TOKEN = "WX_ACCESS_TOKEN_";

    private static final String SUBSCRIBE_REQUEST_URL = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=";
    private static final String PUBLIC_SUBSCRIBE_REQUEST_URL = "https://api.weixin.qq.com/cgi-bin/message/template/subscribe?access_token=";
    private static final String PUBLIC_SUBSCRIBE_TWO_REQUEST_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
    private static final String SUBSCRIBE_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
    private static final String ACCESS_TOKEN_KEY = "access_token-";
    private static final String CREATE_LIVE_ROOM_URL = "https://api.weixin.qq.com/wxaapi/broadcast/room/create";
    private static final String GET_LIVE_ROOM_LIST_URL = "https://api.weixin.qq.com/wxa/business/getliveinfo";
    private static final String GET_LIVE_RECORD_URL = "https://api.weixin.qq.com/wxa/business/getliveinfo";
    private static final String UP_LOAD_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/upload";
    private static final String GET_TEMP_DATA_URL = "https://api.weixin.qq.com/cgi-bin/media/get";

    private static String agencyWxAppId;

    private static String agencyWxAppSecret;

    @Value("${wx.appId}")
    private void setAgencyWxAppId(String wxAppId){
        this.agencyWxAppId=wxAppId;
    }

    @Value("${wx.secret}")
    private void setAgencyWxAppSecret(String wxAppSecret){
        this.agencyWxAppSecret = wxAppSecret;
    }

    //获取token
    public static String getAccessToken(String appId, String secret) {
        String redisKey = WX_ACCESS_TOKEN + appId + "_" + secret;
        String accesstoken = (String) LettuceRedisClientUtils.getValueByKey(redisKey);

        if(StringUtils.isNotEmpty(accesstoken)){
            log.info("从缓存中获取token");
            return accesstoken;
        }
        Map<String, String> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appid", appId);
        param.put("secret", secret);
        ResponseEntity<String> vxResult = restTemplate.getForEntity(SUBSCRIBE_ACCESS_TOKEN_URL
                        +"?grant_type=client_credential&appid="+appId+"&secret="+secret
                ,String.class);
        log.info(vxResult.getBody());

        AccessTokenModel accessTokenModel = JsonUtils.jsonToPojo(vxResult.getBody(), AccessTokenModel.class);
        //采用默认的缓存时长一个半小时
        LettuceRedisClientUtils.setStrKeyValue(redisKey , accessTokenModel.getAccess_token() , 5400);
        return accessTokenModel.getAccess_token();
    }

    //json转map
    public static Map<String, Object> parseJSON2Map(JSONObject json) {
        Map<String, Object> map = new HashMap<String, Object>();
        // 最外层解析
        for (Object k : json.keySet()) {
            Object v = json.get(k);
            // 如果内层还是数组的话，继续解析
            if (v instanceof JSONArray) {
                List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
                @SuppressWarnings("unchecked")
                Iterator<Object> it = ((JSONArray) v).iterator();
                while (it.hasNext()) {
                    JSONObject json2 = (JSONObject) it.next();
                    list.add(parseJSON2Map(json2));
                }
                map.put(k.toString(), list);
            } else {
                map.put(k.toString(), v);
            }
        }
        return map;
    }

    public static Map<String, Object> subscribeMessageSend(String appId, String appSecret,
                                                           String userOpenId, String miniState,
                                                           String templateId, String page,
                                                           Map<String, Object> tmpContent) {

        SubscribeMessageVo data = new SubscribeMessageVo();
        data.setNumber1(new SubscribeMessageVo.Number1((String) tmpContent.get("number1")));
        data.setNumber2(new SubscribeMessageVo.Number2((String) tmpContent.get("number2")));
        WxMsgVo wxMssVO = new WxMsgVo();
        String accessToken = getAccessToken(appId, appSecret);
        wxMssVO.setAccess_token(accessToken);
        wxMssVO.setTouser(userOpenId);
        //模板id号码
        wxMssVO.setTemplate_id(templateId);
        wxMssVO.setData(data);
        wxMssVO.setMiniprogram_state(miniState);
        wxMssVO.setLang("zh_CN");
        wxMssVO.setPage(page);
        String url = SUBSCRIBE_REQUEST_URL + accessToken;
        ResponseEntity<String> vxResult = restTemplate.postForEntity(url, wxMssVO,String.class);
        //把信息封装为json
        JSONObject res = JSONObject.parseObject(vxResult.getBody());
        //把信息封装到map
        Map<String, Object> map = parseJSON2Map(res);
        if (null != map && map.containsKey("errcode")) {
            int errorCode = (int) map.get("errcode");
            String errmsg = (String) map.get("errmsg");
            log.info("微信消息订阅完成，返回码={},返回描述={}", errorCode, errmsg);
            if (errorCode != 0) {
                if(errorCode == 40001){
                    log.info("token过期了");
                    accessToken = updateAccessToken(appId, appSecret);
                    wxMssVO.setAccess_token(accessToken);
                    url = SUBSCRIBE_REQUEST_URL + accessToken;
                    vxResult = restTemplate.postForEntity(url, wxMssVO,String.class);
                    //把信息封装为json
                    res = JSONObject.parseObject(vxResult.getBody());
                    //把信息封装到map
                    map = parseJSON2Map(res);
                }
//                throw new CommonBaseException(ResultCodeEnum.D100);
            }
        }
        return map;
    }

    public static String updateAccessToken(String appId, String secret) {
        String redisKey = WX_ACCESS_TOKEN + appId + "_" + secret;
        Map<String, String> param = new HashMap<>();
        param.put("grant_type", "client_credential");
        param.put("appid", appId);
        param.put("secret", secret);

        ResponseEntity<String> vxResult = restTemplate.getForEntity(SUBSCRIBE_ACCESS_TOKEN_URL
                +"?grant_type=client_credential&appid="+appId+"&secret="+secret,String.class);
        log.info(vxResult.getBody());

        AccessTokenModel accessTokenModel = JsonUtils.jsonToPojo(vxResult.getBody(), AccessTokenModel.class);
        //TODO:这里需要改成缓存到redis上去
        //采用默认的缓存时长一个半小时
        LettuceRedisClientUtils.setAndExpStrKeyValue(redisKey , accessTokenModel.getAccess_token() , 5400);
        return accessTokenModel.getAccess_token();
    }


    //生成分享二维码
    public static void getWxQRCode(String path, String wxAppId, String appSecret, String targetTmpPath) {
        try {
            //现获取token
            String token = getAccessToken(wxAppId, appSecret);
            log.info("获取到的token={}" , token);
            if (StringUtils.isNullOrEmpty(token)) {
                throw new CommonBaseException(ResultCodeEnum.D3014);
            }
            String urlStr = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=" + token;
            InputStream is = null;
            CloseableHttpClient httpClient = null;
            httpClient = HttpClients.createDefault();
            HttpPost httpPost = new HttpPost(urlStr);
            Map<String, Object> jsonMap = new HashMap<>();
            jsonMap.put("path", path);
            log.info("上送给微信生成二维码的内容为:{}",JSONObject.toJSONString(jsonMap));
            StringEntity requestEntity = new StringEntity(JSONObject.toJSONString(jsonMap), "utf-8");
            requestEntity.setContentEncoding("UTF-8");
            httpPost.setHeader("Content-type", "application/json");
            httpPost.setEntity(requestEntity);
            HttpResponse response;
            response = httpClient.execute(httpPost);
            int code = response.getStatusLine().getStatusCode();
            if (HttpStatus.OK.value() == code) {
                is = response.getEntity().getContent();
                /**
                 * 这里可能会出现access_token失效的情况，这时候的返回内容为:
                 * {"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest hints: [dfeCO0XMRa-CADtMa!]"}
                 * **/
                if(response.getEntity().getContentLength() < 1000){
                    log.info("微信生成二维码接口返回内容长度小于1000");
                    String result = IOUtils.toString(response.getEntity().getContent());
                    JSONObject jsonObject = JSONObject.parseObject(result);
                    if(jsonObject.containsKey("errcode")){
                        int errcode = jsonObject.getInteger("errcode");
                        if(errcode == 40001){
                            log.info("token过期了");
                            token = updateAccessToken(wxAppId, appSecret);
                            urlStr = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=" + token;
                            httpPost = new HttpPost(urlStr);
                            jsonMap = new HashMap<>();
                            jsonMap.put("path", path);
                            requestEntity = new StringEntity(JSONObject.toJSONString(jsonMap), "utf-8");
                            requestEntity.setContentEncoding("UTF-8");
                            httpPost.setHeader("Content-type", "application/json");
                            httpPost.setEntity(requestEntity);
                            response = httpClient.execute(httpPost);
                            code = response.getStatusLine().getStatusCode();
                            if (HttpStatus.OK.value() == code) {
                                is = response.getEntity().getContent();
                            }else{
                                throw new CommonBaseException(ResultCodeEnum.D3016);
                            }
                        }else {
                            throw new CommonBaseException(ResultCodeEnum.D3016);
                        }
                    }else {
                        throw new CommonBaseException(ResultCodeEnum.D3016);
                    }
                }else{
                    log.info("微信返回内容长度大于1000");
                }

                File localFile = new File(targetTmpPath);
                FileOutputStream fileOutputStream = new FileOutputStream(localFile);
                IOUtils.copy(is , fileOutputStream);

                IOUtils.closeQuietly(is);
                IOUtils.closeQuietly(fileOutputStream);
            } else {
                throw new CommonBaseException(ResultCodeEnum.D3016);
            }

        } catch (Exception e) {
            log.error("下载微信小程序二维码出现异常", e);
            throw new CommonBaseException(ResultCodeEnum.D3016);
        }
    }




    //创建直播间
    public static JSONObject creatLiveRoom(WxLiveRoomRequestVo wxLiveRoomRequestVo, String appId , String secrect){
        if(null == wxLiveRoomRequestVo){
            return new JSONObject();
        }
        String token = getAccessToken(appId , secrect);
        log.info("获取到的token={}" , token);
        if (StringUtils.isNullOrEmpty(token)) {
            throw new CommonBaseException(ResultCodeEnum.D3014);
        }
        String url = CREATE_LIVE_ROOM_URL + "?access_token=" + token;
        ResponseEntity<String> wxRspStr = restTemplate.postForEntity(url , JSON.toJSONString(wxLiveRoomRequestVo),String.class);
        log.info("创建直播间，微信返回内容为:{}" , wxRspStr.getBody());
        JSONObject wxLiveRoomRspVo = JsonUtils.jsonToPojo(wxRspStr.getBody() , JSONObject.class);
        return wxLiveRoomRspVo;
    }

    //获取直播间列表
    public static JSONObject getLiveRoomList(int start , int limit ,
                                                            String appId , String secrect){
        String token = getAccessToken(appId , secrect);
        log.info("获取到的token={}" , token);
        if (StringUtils.isNullOrEmpty(token)) {
            throw new CommonBaseException(ResultCodeEnum.D3014);
        }
        Map<String ,Object> param = new HashMap<>();
        param.put("start" , start);
        param.put("limit" , limit);
        String url = GET_LIVE_ROOM_LIST_URL + "?access_token=" + token;
        ResponseEntity<String> wxRspStr = restTemplate.postForEntity(url ,  JSON.toJSONString(param),String.class);
        log.info("创建直播间，微信返回内容为:{}" , wxRspStr.getBody());
        JSONObject wxLiveRoomRspVo = JsonUtils.jsonToPojo(wxRspStr.getBody() , JSONObject.class);
        return wxLiveRoomRspVo;
    }

    //新增临时素材
    public static String uploadMedia(String type , File file , String appId , String secret){
        if(org.apache.commons.lang.StringUtils.isBlank(type)){
            //默认上传照片
            type = "image";
        }
        if(null == file){
            log.info("媒体文件为空，无法上传给腾讯微信直播");
            return "";
        }
        String token = getAccessToken(appId , secret);
        log.info("获取到的token={}" , token);
        if (StringUtils.isNullOrEmpty(token)) {
            throw new CommonBaseException(ResultCodeEnum.D3014);
        }

        String url = UP_LOAD_MEDIA + "?access_token=" + token + "&type=" + type;
        String mediaId = null;
        try {
            mediaId = UploadPicTowX.getMediaIdFromUrl(url , file);
        } catch (Exception e) {
            log.error("上传微信出错:{}" , e);
        }
        return mediaId;
    }


    public static void generateMicroAppCode(String scene,String page,String filePath,String fileName) {
        String accessToken = WxOpUtils.getAccessToken(agencyWxAppId, agencyWxAppSecret);
        Map<String, Object> params = new HashMap<>();
        params.put("scene", scene);
        params.put("page", page);
        try {
            ResponseEntity<byte[]> responseEntity = restTemplate.postForEntity("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + accessToken,
                    params, byte[].class);
            String content = new String(responseEntity.getBody());
            log.info(content);
            // 转换失败说明是二进制流
            JSONObject error;
            try {
                error = JSONObject.parseObject(content);
            }catch (Exception e){
                FileUtils.writeFile(filePath,fileName,responseEntity.getBody());
                return;
            }
            // 强制刷新token
            WxOpUtils.updateAccessToken(agencyWxAppId, agencyWxAppSecret);
            throw new RuntimeException(error.getString("errcode").concat(":").concat(error.getString("errmsg")));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(ObjectUtils.isEmpty(e.getMessage()) ? "生成失败！" : e.getMessage());
        }
    }
}
