package cn.hmsoft.weixin.request;

import java.net.InetAddress;
import java.time.LocalDateTime;
import java.util.Map;

import cn.hmsoft.application.SpringHelper;
import cn.hmsoft.application.web.WebAppStart;
import cn.hmsoft.helper.BeanHelper;
import cn.hmsoft.helper.LocalDateHelper;
import cn.hmsoft.helper.LocalDateHelper.LocalDateCompareType;
import cn.hmsoft.helper.SecureHelper;
import cn.hmsoft.helper.StringHelper;
import cn.hmsoft.helper.http.HttpHelper;
import cn.hmsoft.helper.http.HttpRequestEntity;
import cn.hmsoft.log.LogHelper;
import cn.hmsoft.weixin.constants.WxParamName;
import cn.hmsoft.weixin.data.model.WxDefine;
import cn.hmsoft.weixin.service.WxTokenService;
import cn.hmsoft.weixin.util.WeiXinParamUtil;
import cn.hmsoft.weixin.util.WeiXinUtil;

/****************************************
 * 获取token，集群模式下考虑并发的问题
 * 
 * @author zxq
 * @create 2019-08-18 10:00:14
 * @version 2.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
public class TokenRequestThread extends Thread {

	private WxDefine wxDefine;
	/*******************
	 * 是否正在请求
	 */
	private boolean requesting = false;
	private InetAddress addr = null;

	/****************
	 * 本机网络请求地址
	 */
	private HttpRequestEntity tokenRequest = null;

	public TokenRequestThread(String app_id) {

		try {
			addr = InetAddress.getLocalHost();
		} catch (Exception e) {
		}
		this.wxDefine = WeiXinUtil.getWxDefine(app_id);
		String response_addr = this.wxDefine.getParamValue(WxParamName.WebHostname, null);
		String tokenurl = this.wxDefine.getParamValue(WxParamName.TokenCenterUrl, "http://token.hmsoft.cn");
		if (StringHelper.isNotEmpty(response_addr) && StringHelper.isNotEmpty(tokenurl)) {
			// 配置了本机地址，说明是去中心服务器获取
			tokenRequest = new HttpRequestEntity();
			tokenRequest
					.setHttp_url(tokenurl + (tokenurl.endsWith("/") ? "" : "/") + "platform/weixin/token/fetch.htm");
			Map<String, Object> map = BeanHelper.toMap(this.wxDefine);
			map.put("request_key",
					SecureHelper.md5(this.wxDefine.getParamValue(WxParamName.TokenCenterPass, "hm2020")));
			map.put("internet_addr", response_addr);
			map.put("deploy_addr", WebAppStart.getIpAddressAndPort());
			tokenRequest.setRequestMap(map);
			tokenRequest.setRead_timeout(WeiXinParamUtil.HttpConnectTimeoutSeconds * 1000);
			tokenRequest.setConnect_timeout(WeiXinParamUtil.HttpConnectTimeoutSeconds * 1000);
		}

	}

	@Override
	public void run() {
		if (this.tokenRequest != null) {
			// token中心模式
			this.wxDefine.setApp_token_expired(null);
			this.fetchTokenFromCenter();
		} else {
			// 自行获取token
			while (true) {
				this.checkToken();
			}
		}

	}

	/**************************
	 * 去中心服务器获取token
	 */
	private void fetchTokenFromCenter() {
		synchronized (TokenRequestThread.class) {
			this.requesting = true;
			LogHelper.info("------[" + (addr == null ? "" : addr.getHostAddress()) + "]  [" + wxDefine.getWx_name()
					+ "] 向Token中心服务器发出Token请求------");
			// 重新设置请求参数
			this.tokenRequest.getRequestMap().putAll(BeanHelper.toMap(this.wxDefine));
			this.tokenRequest.getRequestMap().remove("app_token_expired");
			int requestTotal = 0;
			do {
				requestTotal++;
				WxDefine wx = HttpHelper.jsonRequest(this.tokenRequest, WxDefine.class);
				if (wx != null && StringHelper.isNotEmpty(wx.getApp_accesstoken())) {
					this.wxDefine.setApp_token_expired(wx.getApp_token_expired());
					this.wxDefine.setApp_accesstoken(wx.getApp_accesstoken());
					this.wxDefine.setApp_jsapi(wx.getApp_jsapi());
					SpringHelper.AppGobalDao.update(this.wxDefine);
					break;
				} else {

					try {
						// 出错后等待3秒
						Thread.sleep(WeiXinParamUtil.TokenIntervalSeconds * 1000);
					} catch (Exception ex) {

					}
				}

			} while (requestTotal < WeiXinParamUtil.TokenRetryTimes);
			if (StringHelper.isEmpty(this.wxDefine.getApp_accesstoken()) || this.wxDefine.getApp_token_expired() == null
					|| this.wxDefine.getApp_token_expired().isBefore(LocalDateTime.now())) {

				LogHelper.error("------[" + (addr == null ? "" : addr.getHostAddress()) + "]  [" + wxDefine.getWx_name()
						+ "] 无法从Token服务器获取令牌，3分钟后重试------");
				try {
					Thread.sleep(180 * 1000);
					this.fetchTokenFromCenter();
				} catch (Exception e) {

				}
			} else {

				LogHelper.info("------[" + (addr == null ? "" : addr.getHostAddress()) + "]  [" + wxDefine.getWx_name()
						+ "] 从Token服务器获取令牌成功------");
			}

			// 请求完毕
			this.requesting = false;
		}

	}

	/*********************************
	 * 检查token
	 */
	public void checkToken() {
		LocalDateTime now = LocalDateTime.now();
		if (StringHelper.isNotEmpty(this.wxDefine.getApp_accesstoken()) && this.wxDefine.getApp_token_expired() != null
				&& this.wxDefine.getApp_token_expired().isAfter(now)) {
			// 还没过期，等待
			try {
				Thread.sleep(LocalDateHelper.calTimeDiff(this.wxDefine.getApp_token_expired(), now,
						LocalDateCompareType.Millis));
			} catch (Exception e) {

			}
		}
		this.refreshToken();
	}

	public void fetchTokenFromWeixin() {
		synchronized (TokenRequestThread.class) {
			this.requesting = true;
			// 过期了，判断是否真的要去获取token
			LocalDateTime request_time = null;
			try {
				request_time = LocalDateHelper.parseDateTime(this.wxDefine.getWx_code());
			} catch (Exception e) {
			}
			if (request_time == null
					|| request_time.plusSeconds(WeiXinParamUtil.TokenWaitSeconds).isBefore(LocalDateTime.now())) {

				LogHelper.info("------[" + (addr == null ? "" : addr.getHostAddress()) + "]  [" + wxDefine.getWx_name()
						+ "] 向微信服务器发出Token请求------");
				// 真的需要去取
				try {
					SpringHelper.getBean(WxTokenService.class).fetchTokenFromWeixin(this.wxDefine, addr);
				} catch (Exception e) {

				}

			}
			this.requesting = false;
		}
		/***********************
		 * 如果没有token,说明获取错误，等待3分钟再去尝试
		 */
		if (StringHelper.isEmpty(this.wxDefine.getApp_accesstoken()) || this.wxDefine.getApp_token_expired() == null
				|| this.wxDefine.getApp_token_expired().isBefore(LocalDateTime.now())) {

			try {
				Thread.sleep(180 * 1000);
			} catch (Exception e) {

			}
		}

	}

	/*********************
	 * 刷新token
	 */
	public void refreshToken() {
		while (this.requesting) {
			try {
				Thread.sleep(1000);
			} catch (Exception e) {

			}
		}
		try {
			WxDefine wx = SpringHelper.AppGobalDao.find(WxDefine.class, "app_id", this.wxDefine.getApp_id());
			this.wxDefine.setApp_token_expired(wx.getApp_token_expired());
			this.wxDefine.setApp_accesstoken(wx.getApp_accesstoken());
			this.wxDefine.setApp_jsapi(wx.getApp_jsapi());
			this.wxDefine.setWx_code(wx.getWx_code());
		} catch (Exception e) {

		}
		LocalDateTime now = LocalDateTime.now();
		if (StringHelper.isNotEmpty(this.wxDefine.getApp_accesstoken()) && this.wxDefine.getApp_token_expired() != null
				&& this.wxDefine.getApp_token_expired().isAfter(now))
			return;
		// 数据库里的也过期了，根据类型去获取
		if (this.tokenRequest != null) {
			// 中心服务器模式
			this.fetchTokenFromCenter();
		} else {
			this.fetchTokenFromWeixin();
		}

	}

}
