package com.yd.util.auth.setting;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

/**
 * jwt 工具类
 * @author
 */
@Service
public class JwtTokenUtil {
    private static final String CLAIM_KEY_SUBJECT = "sub";
//    private static final String CLAIM_KEY_ISSURE = "iss";
//    private static final String CLAIM_KEY_AUDIENCE = "aud";

    private static final String CLAIM_KEY_CREATED = "created";
//    private static final String CLAIM_KEY_BIRTHDAY = "birthDay";
//    private static final String CLAIM_KEY_ADDRESS = "address";

	@Autowired
	private AudienceSetting audienceSetting;

	/**
	 * 从token中获取subject
	 * @param token
	 * @return
	 */
    public String getSubjectFromToken(String token) {
        String subject = null;
        Map<String,Object> map = getClaimsFromToken(token);
        String resultCode = (String)map.get("resultCode");
        if("SUCCESS".equals(resultCode)){
        	Object obj = map.get("claims");
        	if(obj != null){
        		Claims claims = (Claims)obj;
        		subject = claims.getSubject();	
        	}
        }
        return subject;
    }

    /**
     * 获取创建时间
     * @param token
     * @return
     */
    public Date getCreatedDateFromToken(String token) {
    	Date created = null ;
        Map<String,Object> map = getClaimsFromToken(token);
        String resultCode = (String)map.get("resultCode");
        if("SUCCESS".equals(resultCode)){
        	Object obj = map.get("claims");
        	if(obj != null){
        		Claims claims = (Claims)obj;
            	created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
        	}
        }
        return created;
    }

    /**
     * 获取过期时间
     * @param token
     * @return
     */
    public Date getExpirationDateFromToken(String token){
        Date expiration = null;
        Map<String,Object> map = getClaimsFromToken(token);
        String resultCode = (String)map.get("resultCode");
        if("SUCCESS".equals(resultCode)){
        	Object obj = map.get("claims");
        	if(obj != null){
        		Claims claims = (Claims)obj;
        		expiration = claims.getExpiration();
        	}
        }
        return expiration;
    }

    /**
     * check token是否过期
     * @param token
     * @return
     */
    public Boolean isTokenExpired(String token) {
    	boolean result = false;
    	Map<String,Object> map = getClaimsFromToken(token);
        String resultCode = (String)map.get("resultCode");
        if("EXPIRED".equals(resultCode)){
        	result = true;
        }else if("SUCCESS".equals(resultCode)){
        	Date expiration = getExpirationDateFromToken(token);
        	if(expiration != null){
        		result = expiration.before(new Date());	
        	}
        }

        return result;
    }

    /**
     * 检查是否可以被刷新
     * @param token
     * @param lastPasswordReset
     * @return
     */
    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        //如果过期则不可以刷新
        if(isTokenExpired(token)){
        	return false;
        }
        Date created = getCreatedDateFromToken(token);
        if(created == null){
        	return false;
        }
        //如果token是在上次密码重置前生成则不可以刷新
        if(lastPasswordReset != null && created.before(lastPasswordReset)){
        	return false;
        }
        return true;
    }

    /**
     * 刷新token
     * @param token
     * @return
     */
    public String refreshToken(String token) {
    	String refreshedToken = null ;
    	Map<String,Object> map = getClaimsFromToken(token);
    	String resultCode = (String)map.get("resultCode");
    	if("SUCCESS".equals(resultCode)){
    		Object obj = map.get("claims");
        	if(obj != null){
        		Claims claims = (Claims)obj;
        		claims.put(CLAIM_KEY_CREATED, new Date());
            	refreshedToken = generateToken(claims);
        	}
        }
        return refreshedToken;
    }

    /**
     * 根据ticket验证token有效
     * @param ticket
     * @param user
     * @return
     */
    public Boolean validateToken(String token, String ticket) {
    	boolean result = false;
        final String subject = getSubjectFromToken(token);
        if(subject == null){
        	return false;
        }
        if(!isTokenExpired(token)){
        	if(ticket != null && subject.equals(ticket)){
        		result = true;
        	}
        }
        return result;
    }

    /**
     * 判断token是否有效（主要是格式是否有效）
     * @param token
     * @return
     */
    public Boolean validateToken(String token) {
    	boolean result = true;
        Map<String,Object> map = getClaimsFromToken(token);
        String resultCode = (String)map.get("resultCode");
        if("INVALID".equals(resultCode)){
        	result = false;
        }
        return result;
    }

    /**
     * 生成过期时间
     * @return
     */
    private Date generateExpirationDate() {
    	Date expirationDate = new Date(System.currentTimeMillis() + audienceSetting.expiration * 1000);
        return expirationDate;
    }

    /**
     * 根据ticket生成token
     * @param ticket
     * @return
     */
    public String generateToken(String ticket){
    	String token = null;
    	Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_SUBJECT, ticket);
        claims.put(CLAIM_KEY_CREATED, new Date());

        token = generateToken(claims);
        token = audienceSetting.issuer + " " + token;
        return token;
    }

    /**
     * 根据Map 生成token串
     * @param Map<String, Object>claims
     * @return
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, audienceSetting.seal)
                .compact();
    }

    /**
     * 根据token获取Claims,申明内容
     * @param token
     * @return
     */
    private Map<String,Object> getClaimsFromToken(String token){
    	Map<String,Object> map = new HashMap<String,Object>();
    	String resultCode = "SUCCESS";
        Claims claims = null;
        try {
        	JwtParser jwtParser = Jwts.parser().setSigningKey(audienceSetting.seal);
        	Jws<Claims> jwsClaims = jwtParser.parseClaimsJws(token);
            claims = jwsClaims.getBody();
        }catch(ExpiredJwtException e){
        	resultCode = "EXPIRED";
        }catch(UnsupportedJwtException|MalformedJwtException|SignatureException|IllegalArgumentException e){
        	resultCode = "INVALID";
        }catch(Exception e){
        	resultCode = "INVALID";
        }

        map.put("resultCode", resultCode);
        map.put("claims", claims);
        return map;
    }
}
