package com.wechat.service;

import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.wechat.activerequests.responses.WxBaseResponse;
import com.wechat.activerequests.responses.MessageIdResponse;
import com.wechat.common.Constants;
import com.wechat.common.utils.JacksonUtils;
import com.wechat.common.utils.MessageUtils;
import com.wechat.common.utils.NetworkUtils;
import com.wechat.common.utils.StringsUtils;
import com.wechat.common.utils.TLSSigAPIv2;
import com.wechat.dao.AccessTokenRedisDao;
import com.wechat.global.base.dao.RedisTemplateDao;
import com.wechat.global.base.dao.RedisWithExpiryTimeDao;
import com.wechat.model.dto.AccessTokenDto;
import com.wechat.model.dto.WxBaseUserInfoDto;
import com.wechat.model.dto.WxMiniTMsgSendDto;
import com.wechat.model.responseDto.WxMiniCode2SessionResp;

import net.sf.json.JSONObject;

@Service
public class WeiXinProgService {

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

	@Resource
	private AccessTokenRedisDao tokenRedisDao;

	@Resource
	private RedisTemplateDao<WxBaseUserInfoDto> redisWxUserDao;
	
	@Resource
	private RedisWithExpiryTimeDao redisExtDao;


	// 视频小程序-用户唯一凭证
	@Value("#{configProperties['wx.miniprog.video.appid']}")
	private String videoAppId;

	// 视频小程序-用户唯一凭证秘钥
	@Value("#{configProperties['wx.miniprog.video.secret']}")
	private String videoAppSecret;

    //视频小程序-token
    @Value("#{configProperties['wx.miniprog.video.token']}")
    private String             wxMiniprogVideoToken;
    
	// 小程序登陆路径
	@Value("#{configProperties['wx.miniprog.login.url']}")
	private String wxMiniprogLoginUrl;
    
	// 请求access_token路径
	@Value("#{configProperties['weixin.token']}")
	private String wxTokenUrl;

	// 重新获取access_token时间 X秒
	@Value("#{configProperties['weixin.reTime']}")
	private int reTime;
	
    // 腾讯即时视频
    @Value("#{configProperties['wx.video.appid']}")
    private long wxMinVideoAppid;
	
    // 腾讯即时视频
    @Value("#{configProperties['wx.video.secret']}")
    private String wxMinVideoSecret;
    
	// 下发小程序和公众号统一的服务消息
	@Value("#{configProperties['wx.miniprog.tplmsg.url']}")
	private String wxMiniTplmsgUrl;
    
	
	// ACCESS_TOKEN_INFOKEY
	private static String ACCESS_TOKEN_KEY_MINI = "accessTokenMini";
	
	// sessionKey
	private static String REDIS_SESSION_KEY="WX_MINI_SESSION_";

	// 视频用户签名key
	private static String VIDEO_USER_SIG_KEY="VDO_USR_SIG_";

	/**
	 * 小程序登录
	 * 
	 * @return access_token
	 */
	public WxMiniCode2SessionResp getJscodeToSession(String jscode) {
		// 获取accessToken的url
		String url = MessageUtils.getText(wxMiniprogLoginUrl, videoAppId, videoAppSecret, jscode);
		WxMiniCode2SessionResp resp = NetworkUtils.getRemoteResponseAsClass(url.toString(),WxMiniCode2SessionResp.class);
		return resp;
	}


	/**
	 * 基础支持接口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_KEY_MINI);
			if (Float.parseFloat(accessTokeRedisDto.getExpire()) < Float
					.parseFloat(Long.toString(System.currentTimeMillis()))) {
				accessTokenDto = getAccessTokenDto();
				log.debug("*************token过期*****************************");
				tokenRedisDao.updateAccessToken(ACCESS_TOKEN_KEY_MINI, accessTokenDto);
			} else {
				// 使用Redis里的AccessToken
				accessTokenDto = accessTokeRedisDto;
			}
		} catch (Exception e) {
			log.debug("*************token异常*****************************");
			accessTokenDto = getAccessTokenDto();
			tokenRedisDao.insertAccessToken(ACCESS_TOKEN_KEY_MINI, accessTokenDto);
		} finally {
			return accessTokenDto.getAccessToken();
		}
	}
	
	/**
	 * 获取access_token类型的属性
	 * 
	 * @return
	 */
	private AccessTokenDto getAccessTokenDto() {
		// 获取accessToken的url
		StringBuilder url = new StringBuilder(wxTokenUrl + "&appid=");
		url.append(videoAppId);
		url.append("&secret=");
		url.append(videoAppSecret);
		JSONObject json = NetworkUtils.getRemoteResponseAsJson(url.toString());
		if (null != json) {
			String accessToken = json.getString("access_token");
			String newExpire = String.valueOf(reTime * 1000);
			long stime = System.currentTimeMillis() + Long.parseLong(newExpire);
			AccessTokenDto accessTokeDto = new AccessTokenDto();
			accessTokeDto.setAccessToken(accessToken);
			accessTokeDto.setExpire(String.valueOf(stime));
			return accessTokeDto;
		} else {
			return null;
		}
	}

	/**
	 * 更新access_token
	 * 
	 * @return
	 */
	public void updAccessTokenInfo() {
		// AccessToken初期化
		AccessTokenDto accessTokenDto = getAccessTokenDto();
		tokenRedisDao.updateAccessToken(ACCESS_TOKEN_KEY_MINI, accessTokenDto);
		log.debug("强制重取token");
	}
	
	
	/**
	 * 小程序数据解密
	 * @param sessionKey
	 * @param encryptedData
	 * @param iv
	 * @return
	 */
	public JSONObject getDecodeInfo(String sessionKey, String encryptedData, String iv) {
        // 被加密的数据
       byte[] dataByte = Base64.decodeBase64(encryptedData);
       // 加密秘钥
       byte[] keyByte = Base64.decodeBase64(sessionKey);
       // 偏移量
       byte[] ivByte = Base64.decodeBase64(iv);
       try {
           // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
           int base = 16;
           if (dataByte.length % base != 0) {
               int groups = dataByte.length / base + (dataByte.length % base != 0 ? 1 : 0);
               byte[] temp = new byte[groups * base];
               Arrays.fill(temp, (byte) 0);
               System.arraycopy(dataByte, 0, temp, 0, dataByte.length);
               dataByte = temp;
           }
           
           if (keyByte.length % base != 0) {
               int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
               byte[] temp = new byte[groups * base];
               Arrays.fill(temp, (byte) 0);
               System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
               keyByte = temp;
           }
           // 初始化
           Security.addProvider(new BouncyCastleProvider());

           Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
           SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
           AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
           parameters.init(new IvParameterSpec(ivByte));
           cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
           byte[] resultByte = cipher.doFinal(dataByte);
           if (null != resultByte && resultByte.length > 0) {
               String result = new String(resultByte, "UTF-8");
               return JacksonUtils.StringTOJson(result);
           }
           
       } catch (Exception e) {
           e.printStackTrace();
       }
	   return null;
	}
	
	
	/**
	 * session存储 7天过期
	 * @param session
	 * @param openId
	 * @param unionId
	 */
	public void setSessionKey(WxBaseUserInfoDto info) {
		//保存登陆状态7天
		redisWxUserDao.insertDays(REDIS_SESSION_KEY+info.getOpenId(), info, 7);
	}
	
	/**
	 * 获取session
	 * @param openId
	 * @return
	 */
	public WxBaseUserInfoDto getSessionKey(String openId) {
		return redisWxUserDao.select(REDIS_SESSION_KEY+openId);
	}
	
	
	/**
	 * 获取视频使用用户签名
	 */
	public String getVideoUserSig(String userid) {
		String sinStr = redisExtDao.select(VIDEO_USER_SIG_KEY+userid);
		if (StringsUtils.isEmpty(sinStr))
		{
			sinStr = sigVideoUser(userid);
		}
		return sinStr;
	}
	
	
	/**
	 * 设置视频使用用户签名
	 */
	private String sigVideoUser(String userid) {
		TLSSigAPIv2 tlsSig = new TLSSigAPIv2(wxMinVideoAppid,wxMinVideoSecret);
		//1天后过期
		String sin = tlsSig.genUserSig(userid, new Long("86400"));
		//保存签名X秒
		redisExtDao.insert(VIDEO_USER_SIG_KEY+userid, sin, 86000);
		return sin;
	}
	
	/**
	 * 下发小程序和公众号统一的服务消息
	 * @return
	 */
	public String sendUniformMessage(WxMiniTMsgSendDto req) {
		String url = MessageUtils.getText(wxMiniTplmsgUrl, getAccessToken());
		WxBaseResponse response = NetworkUtils.getRemoteResponseAsJson(url, req, WxBaseResponse.class);
		log.debug("************ReqDto: " + JacksonUtils.beautifulSerialize(req));
		log.debug("************Responded: " + JacksonUtils.beautifulSerialize(response));
		// 模板消息推送出错
		if (!StringsUtils.isEqual(Constants.STR_OK, Long.toString(response.getErrorCode()))) {
			log.error("模板消息推送异常：ErrorMsg=" + response.getErrorMsg() + "; ErrorCode=" + response.getErrorCode());
			// 40001 的情况下强制重取token
			if (StringsUtils.isEqual(Constants.STR_TOKEN_ERO, Long.toString(response.getErrorCode()))) {
				updAccessTokenInfo();
				url = MessageUtils.getText(wxMiniTplmsgUrl, getAccessToken());
			}
			// 重新发送
			response = NetworkUtils.getRemoteResponseAsJson(url, req, MessageIdResponse.class);
			log.error("模板消息重发：ErrorMsg=" + response.getErrorMsg() + "; ErrorCode=" + response.getErrorCode());
		}
		String rest = response.getErrorCode().toString();
		return rest;
	}

}
