package com.dbnt.kcscbackend.config.jwt;


import com.dbnt.kcscbackend.auth.entity.UserInfo;
import com.dbnt.kcscbackend.config.egov.EgovProperties;
import com.dbnt.kcscbackend.config.jwt.redis.RefreshToken;
import com.dbnt.kcscbackend.config.jwt.redis.RefreshTokenRepository;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 *
 * @author : 정완배
 * @since : 2023. 8. 9.
 * @version : 1.0
 *
 * @package : egovframework.com.jwt
 * @filename : EgovJwtTokenUtil.java
 * @modificationInformation
 *
 * <pre>
 * << 개정이력(Modification Information) >>
 *
 *  수정일            수정자             수정내용
 *  ----------   ----------   ----------------------
 *  2023. 8. 9.    정완배              주석추가
 * </pre>
 *
 *
 */
//security 관련 제외한 jwt util 클래스
@Slf4j
@RequiredArgsConstructor
@Component
public class EgovJwtTokenUtil implements Serializable{

  private static final long serialVersionUID = -5180902194184255251L;
  public static final String SECRET_KEY = EgovProperties.getProperty("Globals.jwt.secret");

  private final RefreshTokenRepository refreshTokenRepository;
  public static final long JWT_ACCESS_VALID_TIME = (30*60*1000); //엑세스 토큰의 유효시간 설정, 30분
  public static final long JWT_REFRESH_VALID_TIME = (60*60*24*14*1000); //리프레시 토큰의 유효시간 설정, 2주


  //retrieve username from jwt token
  public String getUserSeqFromToken(String token) {
    Claims claims = getClaimFromToken(token);
    return claims.get("userSeq").toString();
  }
  public String getUserIdFromToken(String token) {
    Claims claims = getClaimFromToken(token);
    return claims.get("id").toString();
  }
  public String getUserSeFromToken(String token) {
    Claims claims = getClaimFromToken(token);
    return claims.get("userSe").toString();
  }
  public String getInfoFromToken(String type, String token) {
    Claims claims = getClaimFromToken(token);
    return claims.get(type).toString();
  }
  public Claims getClaimFromToken(String token) {
    return getAllClaimsFromToken(token);
  }

  //for retrieveing any information from token we will need the secret key
  public Claims getAllClaimsFromToken(String token) {
    log.debug("===>>> secret = "+SECRET_KEY);

    return Jwts.parserBuilder().setSigningKey(SECRET_KEY.getBytes(StandardCharsets.UTF_8)).build().parseClaimsJws(token).getBody();
  }

  //generate token for user
  //while creating the token -
  //1. Define  claims of the token, like Issuer, Expiration, Subject, and the ID
  //2. Sign the JWT using the HS512 algorithm and secret key.
  //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1)
  //   compaction of the JWT to a URL-safe string
  public String generateToken(UserInfo loginVO, String remoteAddr, Long sec) {
    Map<String, Object> claims = new HashMap<>();
    claims.put("userSeq", loginVO.getUserSeq());
    claims.put("id", loginVO.getUserId());
    claims.put("remoteAddr", remoteAddr);
    claims.put("userSe", loginVO.getUserSe() );
    claims.put("type", "Authorization");

    log.debug("===>>> secret = "+SECRET_KEY);

    JwtBuilder builder = Jwts.builder()
        .setClaims(claims)
        .setSubject("Authorization")
        .setIssuedAt(new Date(System.currentTimeMillis()))
        .setExpiration(new Date(System.currentTimeMillis() + sec))
        .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS512);
    return builder.compact();
  }

  public String generateAccessToken(UserInfo loginVO, String remoteAddr) {
    return generateToken(loginVO, remoteAddr, JWT_ACCESS_VALID_TIME);
  }

  @Transactional
  public String generateRefreshTokenToken(UserInfo loginVO, String remoteAddr){
    String token = generateToken(loginVO, remoteAddr, JWT_REFRESH_VALID_TIME);
    RefreshToken refreshToken = new RefreshToken(loginVO.getUserSeq(), token);
    refreshTokenRepository.save(refreshToken);
    return refreshToken.getRefreshToken();
  }

  public String accessTokenRefresh(String token){
    RefreshToken refreshToken = refreshTokenRepository.findByRefreshToken(token).orElse(null);
    if(refreshToken!=null){
      Claims claims = getClaimFromToken(refreshToken.getRefreshToken());
      JwtBuilder builder = Jwts.builder()
          .setClaims(claims)
          .setSubject("Authorization")
          .setIssuedAt(new Date(System.currentTimeMillis()))
          .setExpiration(new Date(System.currentTimeMillis() + JWT_ACCESS_VALID_TIME))
          .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes(StandardCharsets.UTF_8)), SignatureAlgorithm.HS512);
      return builder.compact();
    }
    return null;
  }

}
