package cn.service.token;

import cn.component.RedisKit;
import cn.constant.CommonConst;
import cn.orm.abs.ISqlToyDao;
import cn.orm.sqltoy.plus.Wrappers;
import cn.orm.sqltoy.plus.query.QueryWrapper;
import cn.pojo.entity.TSysUser;
import cn.pojo.vo.LoginUser;
import cn.util.core.IsKit;
import cn.util.core.TypeKit;
import cn.util.http.IpKit;
import cn.util.core.ServletKit;
import cn.util.uuid.UUIDKit;
import eu.bitwalker.useragentutils.UserAgent;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

/**token验证处理*/
@Component
public class JwtTokenService 
{
	//令牌有效期（默认30分钟）
	@Value("${sam.token.expireTime}")
	private Integer expireTime;

	@Autowired
	private RedisKit redisCache;

	@Autowired
	private ISqlToyDao daoService;

	/** APP专用
	 * 生成签名,5min后过期
	 * @param username 用户名
	 * @param secret   用户的密码
	 * @return 加密的token
	 */
	public String appSign(String username, String secret) {
		Date date = new Date(System.currentTimeMillis() + CommonConst.EXPIRE_TIME);
		Algorithm algorithm = Algorithm.HMAC256(secret);
		// 附带username信息
		return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
	}
	/**根据request中的token获取用户账号*/
	public String getAppUserName(HttpServletRequest request)
	{
		String accessToken = request.getHeader("X-Access-Token");
		String username = getAppUserName(accessToken);
		if (IsKit.isNullOrEmpty(username)) {
			return "";
		}
		return username;
	}

	/**
	 * 获得token中的信息无需secret解密也能获得
	 *
	 * @return token中包含的用户名
	 */
	public String getAppUserName(String token) {
		try {
			DecodedJWT jwt = JWT.decode(token);
			return jwt.getClaim("username").asString();
		} catch (JWTDecodeException e) {
			return null;
		}
	}
	public TSysUser getAppUser(String token){
		try {
			DecodedJWT jwt = JWT.decode(token);
			String userName = jwt.getClaim("username").asString();
			QueryWrapper<TSysUser> queryWrapper = Wrappers.wrapper(TSysUser.class)
					.eq("status",CommonConst.CHAR_NUM_0)//0true正常 1停用
					.eq("delFlag",CommonConst.CHAR_NUM_0)//0代表存在  2代表删除
					.eq("userName", userName);
			return daoService.findOne(queryWrapper);
		} catch (JWTDecodeException e) {
			return null;
		}
	}
	
	//////////以上为APP，以下为PC端////////////

	/** 从令牌中获取用户名 */
	public String getUserName(String token)
	{
		Claims claims = parseToken(token);
		return claims.getSubject();
	}
	// 获取请求携带的令牌
	public LoginUser getLoginUser(HttpServletRequest request) 
	{
		return getLoginUser( getToken(request));
	}
	
	public LoginUser getLoginUser(String token) 
	{
		if (StringUtils.isNotEmpty(token)) {
			Claims claims = parseToken(token);
			// 解析对应的权限以及用户信息
			String id = TypeKit.objToStr(claims.get(CommonConst.LOGIN_USER_KEY));
			String userKey = getTokenKey(id);
			Object cacheObject = redisCache.getCacheObject(userKey);
			if(IsKit.isNotNullOrEmpty(cacheObject)) {
				return (LoginUser)cacheObject;
			}
		}
		return null;
	}
	

	/**
	 * 为每个登录用户创建令牌 
	 * @param loginUser 用户信息
	 * @return 令牌
	 */
	public String createToken(LoginUser loginUser) {
		String token = UUIDKit.fastUUID_S();
		loginUser.setToken(token);
		_setUserAgent(loginUser);
		refreshToken(loginUser);
		Map<String, Object> claims = new HashMap<>();
		claims.put(CommonConst.LOGIN_USER_KEY, token);
		return _createToken(claims);
	}
	/**
	 * 验证令牌有效期，相差不足20分钟，自动刷新缓存
	 * @param loginUser 登陆对象	 
	 */
	public void verifyToken(LoginUser loginUser)
	{
		long expireTime = loginUser.getExpireTime();
		long currentTime = System.currentTimeMillis();
		if (expireTime - currentTime <= 20 * CommonConst.MILLIS_MINUTE) {
			refreshToken(loginUser);
		}
	}
	
	/** 依据数据声明claims生成令牌 */
	private String _createToken(Map<String, Object> claims) {
		String token = Jwts.builder()
				.setClaims(claims)
				.signWith(SignatureAlgorithm.HS512, CommonConst.TOKEN_SECRET)
				.compact();
		return token;
	}

	/**设置用户代理信息*/
	private void _setUserAgent(LoginUser loginUser) {
		UserAgent userAgent = UserAgent.parseUserAgentString(ServletKit.getRequest().getHeader("User-Agent"));
		String ip = IpKit.getIpAddr(ServletKit.getRequest());
		loginUser.setIpaddr(ip);
		loginUser.setLoginLocation(IpKit.getRealAddressByIP(ip));
		loginUser.setBrowser(userAgent.getBrowser().getName());
		loginUser.setOs(userAgent.getOperatingSystem().getName());
	}
	
	/**刷新令牌有效期*/
	public void refreshToken(LoginUser loginUser)
	{
		if(IsKit.isNullOrEmpty(expireTime)) {
			expireTime = CommonConst.TOKEN_SCALE;
		}
		loginUser.setLoginTime(System.currentTimeMillis());
		loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * CommonConst.MILLIS_MINUTE);
		//缓存loginUser
		String userKey = getTokenKey(loginUser.getToken());
		redisCache.setCacheObject(userKey,loginUser,expireTime, TimeUnit.MINUTES);
	}
	public String getTokenKey(String id) {
		return CommonConst.LOGIN_TOKEN_KEY + id;
	}
	
	/**获取请求token*/
	public String getToken(HttpServletRequest request)
	{
		String token = request.getHeader(CommonConst.TOKEN_HEADER);
		if (StringUtils.isNotEmpty(token) && token.startsWith(CommonConst.TOKEN_PREFIX)) {
			token = token.replace(CommonConst.TOKEN_PREFIX, "");
		}
		return token;
	} 
	/**
	 * 从令牌中获取数据声明
	 * @param token 令牌
	 * @return 数据声明
	 */
	public Claims parseToken(String token) {
		if(IsKit.isNullOrEmpty(token)) {
			return null;
		}
		return Jwts.parser()
				.setSigningKey(CommonConst.TOKEN_SECRET)
				.parseClaimsJws(token)
				.getBody();
	}
}