package com.zhgd.xmgl.security;

import com.zhgd.exception.CustomException;
import com.zhgd.redis.lock.RedisRepository;
import com.zhgd.xmgl.modules.basicdata.constant.CacheConstants;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: devManage
 * @description: JWTtoken生成工具
 * @author: Mr.Peng
 * @create: 2019-09-24 10:46
 **/
@Component
public class JwtTokenProvider {
    @Value("${security.jwt.token.secret-key}")
    private String secretKey;

    private static ConcurrentHashMap<String, String> userMap = new ConcurrentHashMap<>();

    @Autowired
    private MyUserDetailsImpl myUserDetailsImpl;

    @Autowired
    private RedisRepository redisRepository;

    @PostConstruct
    protected void init() {
        secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    /**
     * @param username
     * @param validityInMilliseconds 单位是秒
     * @return
     */
    public String createToken(String username, Integer validityInMilliseconds) {
        Claims claims = Jwts.claims().setSubject(username);
        Date now = new Date();
        Date validity = new Date(now.getTime() + validityInMilliseconds * 1000);
        String token = Jwts.builder()//
                .setClaims(claims)//
                .setIssuedAt(now)//
                .setExpiration(validity)//过期时间秒
                .signWith(SignatureAlgorithm.HS256, secretKey)//
                .compact();
        userMap.put(username, token);
        return token;
    }

    public Authentication getAuthentication(String token) {
        String username = getUsername(token);
        UserDetails userDetails = (UserDetails) redisRepository.get(CacheConstants.USER_DETAILS + username);
        if (userDetails == null) {
            userDetails = myUserDetailsImpl.loadUserByUsername(getUsername(token));
            redisRepository.set(CacheConstants.USER_DETAILS + username, userDetails/*, 600, TimeUnit.SECONDS*/);
        }
        return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }

    public String getUsername(String token) {
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
    }

    public String resolveToken(HttpServletRequest req) {
        //String bearerToken=req.getParameter("token");
        String bearerToken = req.getHeader("Authorization");
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return bearerToken;
    }

    public boolean validateToken(String token) {
        try {
            Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            if (claims.getBody().getExpiration().before(new Date())) {
                return false;
            }
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            throw new CustomException("Expired or invalid JWT token", HttpStatus.UNAUTHORIZED);
            //throw new CustomException("Expired or invalid JWT token", HttpStatus.OK);
            //throw new OpenAlertException(403,"Expired or invalid JWT token");
        }
    }

    public void valiadteLogin(String token) {
        String userName = getUsername(token);
        if (userMap.containsKey(userName)) {
            if (!token.equals(userMap.get(userName))) {
                throw new CustomException("该账户已其他地方登录", HttpStatus.UNAUTHORIZED);
            }
        } else {
            userMap.put(userName, token);
        }
    }

    /**
     * @param username
     * @return
     */
    public void removeToken(String username) {
        try {
            redisRepository.del(CacheConstants.USER_DETAILS + username);
            userMap.remove(username);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
