package com.wechat.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Date;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.wechat.common.Constants;
import com.wechat.common.utils.MessageUtils;
import com.wechat.common.utils.NetworkUtils;
import com.wechat.common.utils.StringsUtils;
import com.wechat.dao.AccessTokenRedisDao;
import com.wechat.dao.JsapiTicketRedisDao;
import com.wechat.model.dto.AccessTokenDto;
import com.wechat.model.dto.JsapiTicketDto;
import com.wechat.model.dto.WxShortUrlDto;
import com.wechat.model.dto.WxUserInfoDto;
import com.wechat.model.requestDto.WxServiceTextMsgContentDto;
import com.wechat.model.requestDto.WxServiceTextMsgReq;
import com.wechat.model.requestDto.WxShortUrlReq;

import net.sf.json.JSONObject;

@Service
public class WeiXinService {

	private static final Log log = LogFactory.getLog(WeiXinService.class);

	@Resource
	AccessTokenRedisDao tokenRedisDao;

	@Resource
	JsapiTicketRedisDao ticketRedisDao;

	// 用户唯一凭证
	@Value("#{configProperties['weixin.appid']}")
	private String appId;

	// 用户唯一凭证秘钥
	@Value("#{configProperties['weixin.secret']}")
	private String appSecret;

	// 请求access_token路径
	@Value("#{configProperties['weixin.token']}")
	private String wxTokenUrl;

	// 重新获取access_token时间
	@Value("#{configProperties['weixin.reTime']}")
	private String reTime;

	// 重新获取jsapiTicket时间
	@Value("#{configProperties['weixin.jsapi.reTime']}")
	private String wxJsapiReTime;

	// 请求jsapiTicket路径
	@Value("#{configProperties['weixin.jsapi.ticket']}")
	private String wxJsapiTicketUrl;
	
	// 请求用户信息路径
	@Value("#{configProperties['weixin.userinfo']}")
	private String wxUserInfoUrl;
	
	// 批量请求用户信息路径
	@Value("#{configProperties['weixin.userinfo.list']}")
	private String wxUserInfoListUrl;
	
	// 上传根路径
	@Value("#{configProperties['upload.base.path']}")
	private String updBaseUrl;
	
	@Value("#{configProperties['upload.path.image']}")
	private String basePackagePathImage;

	
    //发送客服消息连接
    @Value("#{configProperties['weixin.service.message']}")
    private String             urlServiceMessage;
    
	// 请求用户信息路径
	@Value("#{configProperties['weixin.shorturl']}")
	private String wxShortUrl;

	// access_token
	private static String access_token;

	// ACCESS_TOKEN_INFOKEY
	private static final String ACCESS_TOKEN_INFOKEY = "accessToken";

	private static final String JSAPI_TICKET = "jsapiTicket";

	public WeiXinService() {
		if (instance != null)
			log.warn("Duplicated init of QQService. Overwriting instance.");
		instance = this;
	}

	private static WeiXinService instance = null;

	public static WeiXinService getInstance() {
		if (instance == null) {
			instance = new WeiXinService();
		}
		return instance;
	}

	/**
	 * 基础支持接口token的获取
	 * 
	 * @return access_token
	 */
	@SuppressWarnings("finally")
	public String getAccessToken() {
		// AccessToken初期化
		AccessTokenDto accessTokenDto = null;
		// Redis AccessToken初期化
		AccessTokenDto accessTokeRedisDto = new AccessTokenDto();
		// 从Redis里取出AccessToken
		try {
			accessTokeRedisDto = tokenRedisDao.selectAccessToken(ACCESS_TOKEN_INFOKEY);
			if (Float.parseFloat(accessTokeRedisDto.getExpire()) < Float
					.parseFloat(Long.toString(System.currentTimeMillis()))) {
				accessTokenDto = getAccessTokenDto();
				tokenRedisDao.updateAccessToken(ACCESS_TOKEN_INFOKEY, accessTokenDto);
			} else {
				// 使用Redis里的AccessToken
				accessTokenDto = accessTokeRedisDto;
			}
		} catch (Exception e) {
			accessTokenDto = getAccessTokenDto();
			tokenRedisDao.insertAccessToken(ACCESS_TOKEN_INFOKEY, accessTokenDto);
		} finally {
			return accessTokenDto.getAccessToken();
		}
	}

	/**
	 * 获取access_token类型的属性
	 * 
	 * @return
	 */
	private AccessTokenDto getAccessTokenDto() {
		// 获取accessToken的url
		StringBuilder url = new StringBuilder(wxTokenUrl + "&appid=");
		url.append(appId);
		url.append("&secret=");
		url.append(appSecret);
		JSONObject json = processRequest(url.toString());
		if (null != json) {
			access_token = json.getString("access_token");
			String newExpire = String.valueOf(Integer.parseInt(reTime) * 1000);
			long stime = System.currentTimeMillis() + Long.parseLong(newExpire);
			AccessTokenDto accessTokeDto = new AccessTokenDto();
			accessTokeDto.setAccessToken(access_token);
			accessTokeDto.setExpire(String.valueOf(stime));
			return accessTokeDto;
		} else {
			return null;
		}
	}

	/**
	 * 更新access_token
	 * 
	 * @return
	 */
	public void updAccessTokenInfo() {
		// AccessToken初期化
		AccessTokenDto accessTokenDto = getAccessTokenDto();
		tokenRedisDao.updateAccessToken(ACCESS_TOKEN_INFOKEY, accessTokenDto);
		log.info("强制重取token");
	}

	/**
	 * 基础支持jsapi_ticket的获取
	 * 
	 * @return access_token
	 */
	@SuppressWarnings("finally")
	public String getJsapiTicket() {
		// JsapiTicket初期化
		JsapiTicketDto ticketDto = new JsapiTicketDto();
		// Redis JsapiTicket初期化
		JsapiTicketDto ticketRedisDto = new JsapiTicketDto();
		// 从Redis里取出JsapiTicket
		try {
			ticketRedisDto = ticketRedisDao.selectJsapiTicket(JSAPI_TICKET);
			if (Float.parseFloat(ticketRedisDto.getExpire()) < Float
					.parseFloat(Long.toString(System.currentTimeMillis()))) {
				// 失效，重新获取
				ticketDto = getJsapiTicketFromWx();
				ticketRedisDao.updateJsapiTicket(JSAPI_TICKET, ticketDto);
			} else {
				// 使用Redis里的AccessToken
				ticketDto = ticketRedisDto;
			}
		} catch (Exception e) {
			ticketDto = getJsapiTicketFromWx();
			ticketRedisDao.insertJsapiTicket(JSAPI_TICKET, ticketDto);
		} finally {
			return ticketDto.getJsapiTicket();
		}
	}

	/**
	 * 从微信接口取得jsapi_ticket
	 * 
	 * @return access_token
	 */
	public JsapiTicketDto getJsapiTicketFromWx() {
		JSONObject json = new JSONObject();
		JsapiTicketDto ticketDto = new JsapiTicketDto();
		try {
			json = NetworkUtils.getRemoteResponseAsJson(MessageUtils.getText(wxJsapiTicketUrl, getAccessToken()));
			//非正常情况
			if(!StringsUtils.isEqual(json.getString("errcode"), Constants.STR_OK))
			{
				throw new Exception();
			}
		} catch (Exception e) {
			// 出错的话，重新取accesstoken后重新获取
			updAccessTokenInfo();
			json = NetworkUtils.getRemoteResponseAsJson(MessageUtils.getText(wxJsapiTicketUrl, getAccessToken()));
		}
		// 重新计算刷新时间
		String newExpire = String.valueOf(Integer.parseInt(wxJsapiReTime) * 1000);
		long stime = System.currentTimeMillis() + Long.parseLong(newExpire);
		ticketDto.setJsapiTicket(json.getString("ticket"));
		ticketDto.setExpire(String.valueOf(stime));

		return ticketDto;
	}

	/**
	 * 更新jsapi_ticket
	 * 
	 * @return
	 */
	public void updJsapiTicketInfo() {
		// AccessToken初期化
		JsapiTicketDto ticketDto = getJsapiTicketFromWx();
		ticketRedisDao.updateJsapiTicket(JSAPI_TICKET, ticketDto);
	}

	/**
	 * 请求处理
	 * 
	 * @param url
	 * @return 处理完毕返回的数据
	 */
	public static JSONObject processRequest(String url) {
		URL getUrl;
		BufferedReader reader = null;
		try {
			getUrl = new URL(url.toString());
			HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type", "application/json");
			connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
			connection.setDoOutput(true);

			reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
			String lines;
			StringBuilder buf = new StringBuilder();
			while ((lines = reader.readLine()) != null) {
				buf.append(lines);
			}
			reader.close();
			JSONObject json = JSONObject.fromObject(buf.toString());
			return json;
		} catch (Exception e) {
			log.error(e);
			return null;
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				log.error(e);
			}
		}
	}
	
    /**
     * 拉取用户信息
     * 
     * @return
     */
    public WxUserInfoDto getUserInfo(String openId) {
	    // 拉取用户信息
	    String userInfoUrl = MessageUtils.getText(wxUserInfoUrl, getAccessToken(),openId);
	    WxUserInfoDto userInfoDto = NetworkUtils.getRemoteResponseAsClass(userInfoUrl, WxUserInfoDto.class);

        return userInfoDto;
    }

    
    /**
     * 根据文件id下载文件
     * @param mediaId 媒体id
     * @throws Exception
     */

    public InputStream getImgFromWx(String mediaId) {
    	//获取token
        String accessToken = getAccessToken();

        InputStream is = null;
        String url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=" + accessToken + "&media_id=" + mediaId;

        try {
        	
        	URL obj = new URL(url);
            HttpURLConnection con = (HttpURLConnection) obj.openConnection();

            //add reuqest header
            con.setRequestMethod("GET");
            con.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3");
            con.setDoOutput(true);


            // 获取文件转化为byte流
            is = con.getInputStream();

        } catch (Exception e) {
        	log.error("获取微信服务器文件异常", e);
            return null;
        }
        return is;
    }
    
    /**

     * 获取下载图片信息（jpg）
     * 
     * @param mediaId 文件的id
     * @throws Exception
     */
   public String saveImageToDisk(String mediaId, String path) throws Exception {
       InputStream inputStream = getImgFromWx(mediaId);
       byte[] data = new byte[1024];
       int len = 0;
       FileOutputStream fileOutputStream = null;
       //文件名为时间戳+ 5位随机字符
       String fileName = StringsUtils.parseLong((new Date()).getTime()) + StringsUtils.getRandomString(5)+ ".jpg";
       try {
    	   String filePath = updBaseUrl + basePackagePathImage + path;
    	   File dirFile = new File(filePath);
    	   //路径不存在的情况创建
           if(!dirFile.exists()){
               if(!dirFile.mkdir())
               {
            	   log.error("路径创建失败");
                   return null; 
               }
            }
           
           fileOutputStream = new FileOutputStream(filePath + fileName);
           while ((len = inputStream.read(data)) != -1) {
               fileOutputStream.write(data, 0, len);
           }
       } catch (IOException e) {
           log.error("微信服务器图标保存失败",e);
           return null;
       } finally {
           if (inputStream != null) {
               inputStream.close();
           }
           if (fileOutputStream != null) {
               fileOutputStream.close();
           }
       }
       return basePackagePathImage + path + fileName;
   }
   
   
	/**
	 * 发送客服消息
	 * 
	 * @param url
	 * @return 处理完毕返回的数据
	 */
	public String sendServiceMsg(String openId, String content) {
		String url = MessageUtils.getText(urlServiceMessage, getAccessToken());
		return NetworkUtils.getRemoteResponseAsJson(url, new WxServiceTextMsgReq(openId,new WxServiceTextMsgContentDto(content)));
	}
	
	/**
	 * 长链接转短链接
	 * 
	 * @param url
	 * @return 处理完毕返回的数据
	 */
	public String getShortUrl(String longUrl) {	
		String url = MessageUtils.getText(wxShortUrl, getAccessToken());
		WxShortUrlReq shortReq = new WxShortUrlReq();
		shortReq.setLong_url(longUrl);
		WxShortUrlDto shortUrlRsep = NetworkUtils.getRemoteResponseAsJson(url,shortReq, WxShortUrlDto.class);
		//token过期的情况
    	if(StringsUtils.isEqual(shortUrlRsep.getErrorCode().toString(), Constants.STR_TOKEN_ERO))
    	{
    		//强制更新token
    		updAccessTokenInfo();
    		log.error("重取获取短链接---" + new Date());
    		//取得短链接
    		url = MessageUtils.getText(wxShortUrl, getAccessToken());
    		shortUrlRsep = NetworkUtils.getRemoteResponseAsJson(url,shortReq, WxShortUrlDto.class);
    	}
    	
    	return shortUrlRsep.getShortUrl();
	}



}
