package pwc.taxtech.atms.security;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import io.jsonwebtoken.*;
import org.nutz.lang.Times;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.impl.DefaultJws;
import io.jsonwebtoken.lang.Assert;
import pwc.taxtech.atms.common.AtmsApiSettings;
import pwc.taxtech.atms.common.CommonUtils;

@Component
public class JwtUtil implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);

    @Autowired
    private AtmsApiSettings atmsApiSettings;
    @Value("${jwt.expireSecond}")
    private Integer jwtExpireSecond;

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(atmsApiSettings);
        Assert.hasText(atmsApiSettings.getJwtBase64Secret(), "Missing settings of jwt.secret");
        jwtBase64Secret = atmsApiSettings.getJwtBase64Secret();
        jwtPowerToken = atmsApiSettings.getJwtPowerToken();
    }

    /**
     * The BASE64-encoded algorithm-specific signing key to use to digitally sign
     * the JWT
     */
    private String jwtBase64Secret;

    private String jwtPowerToken;

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public JwtUser parseToken(String token) {
        if (StringUtils.hasText(jwtPowerToken) && jwtPowerToken.equals(token)) {
            return new JwtUser("test_userid", "admin", "Admin", null, getAuthorities());
        }
        JwtParser parser = Jwts.parser().setSigningKey(jwtBase64Secret);
        Jwt jwt = parser.parseClaimsJws(token);
        DefaultJws<DefaultClaims> defaultJws = (DefaultJws<DefaultClaims>) jwt;
        DefaultClaims defaultClaims = defaultJws.getBody();
        String databaseUsername = String.valueOf(defaultClaims.get("databaseUsername"));
        String username = String.valueOf(defaultClaims.get("username"));
        String userid = String.valueOf(defaultClaims.get("userid"));

        return new JwtUser(userid, username, databaseUsername, defaultClaims, getAuthorities());
    }

    private List<SimpleGrantedAuthority> getAuthorities() {
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_USER"));
        list.add(new SimpleGrantedAuthority("ROLE_JWT_USER"));
        return list;
    }

    /***
     * @param username
     *            登录名,大小写不限,可以是全大写或全小写,如:admin, ADMIN
     * @param databaseUsername
     *            数据库用户名, 比如Admin
     * @param userid
     *            用户Id
     * @return
     */
    public String generateToken(String username, String databaseUsername, String userid) {
        // sub: 该JWT所面向的用户
        // iss: 该JWT的签发者
        // iat(issued at): 在什么时候签发的token
        // exp(expires): token什么时候过期
        // nbf(not before):token在此时间之前不能被接收处理
        // jti:JWT Id为web token提供唯一标识

        Date now = new Date();
        // 过期时间设置为2天
        int expireSecond = jwtExpireSecond;
        Date expiration = Times.nextSecond(now, expireSecond);
        JwtBuilder jwtBuilder = Jwts.builder();
        // 设置Subject为登录用户名
        jwtBuilder.setSubject(username);
        jwtBuilder.setExpiration(expiration);
        jwtBuilder.setIssuedAt(now);
        // 设置时钟误差偏移量,即10分钟
        Date notBefore = Times.nextSecond(now, -600);
        jwtBuilder.setNotBefore(notBefore);
        jwtBuilder.setId(CommonUtils.getUUID());
        jwtBuilder.claim("username", username);
        jwtBuilder.claim("databaseUsername", databaseUsername);
        jwtBuilder.claim("userid", userid);
        // 设置body.username为数据库用户名
        jwtBuilder.signWith(SignatureAlgorithm.HS512, jwtBase64Secret);
        return jwtBuilder.compact();
    }

    public String getJwtBase64Secret() {
        return jwtBase64Secret;
    }

    public void setJwtBase64Secret(String jwtBase64Secret) {
        this.jwtBase64Secret = jwtBase64Secret;
    }

    public String getJwtPowerToken() {
        return jwtPowerToken;
    }

    public void setJwtPowerToken(String jwtPowerToken) {
        this.jwtPowerToken = jwtPowerToken;
    }
}