package com.spring.infra.security.jwt;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import com.spring.infra.security.domain.UserPrincipal;

import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;

/**
 * JWT 토큰을 생성하는 클래스입니다.
 * 
 * <p>이 클래스는 액세스 토큰과 리프레시 토큰을 생성하는 기능을 제공합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
@RequiredArgsConstructor
public class JwtTokenGenerator {
    
    private final JwtProperties jwtProperties;
    private final JwtTokenUtil jwtTokenUtil;

    /**
     * 액세스 토큰을 생성합니다.
     * 
     * @param authentication 인증 정보
     * @return 생성된 액세스 토큰
     */
    public String generateAccessToken(Authentication authentication) {
        UserPrincipal user = (UserPrincipal) authentication.getPrincipal();
        return Jwts.builder()
                .setHeader(createHeader())
                .setClaims(createClaims(user))
                .setSubject(user.getUsername())
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(Date.from(Instant.now().plus(jwtProperties.getAccessToken().getExpiration(), ChronoUnit.MINUTES))) 
                .signWith(jwtTokenUtil.getSigningKey(jwtProperties.getAccessToken().getSecret()))
                .compact();
    }

    /**
     * 리프레시 토큰을 생성합니다.
     * 
     * @param authentication 인증 정보
     * @return 생성된 리프레시 토큰
     */
    public String generateRefreshToken(Authentication authentication) {
        UserPrincipal user = (UserPrincipal) authentication.getPrincipal();
        return Jwts.builder()
                .setSubject(user.getKey())
                .setIssuedAt(Date.from(Instant.now()))
                .setExpiration(Date.from(Instant.now().plus(jwtProperties.getRefreshToken().getExpiration(), ChronoUnit.MINUTES))) 
                .signWith(jwtTokenUtil.getSigningKey(jwtProperties.getRefreshToken().getSecret()))
                .compact();
    }

    /**
     * JWT 헤더를 생성합니다.
     * 
     * @return JWT 헤더 맵
     */
    private Map<String, Object> createHeader() {
        Map<String, Object> header = new HashMap<>();
        header.put("typ", "JWT");
        header.put("alg", "HS512");
        return header;
    }

    /**
     * JWT 클레임을 생성합니다.
     * 
     * @param authentication 인증 정보
     * @return JWT 클레임 맵
     */
    private Map<String, Object> createClaims(UserPrincipal user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtTokenRule.USER_ID.getValue(), user.getUsername());
        claims.put(JwtTokenRule.USER_NAME.getValue(), user.getMemberName());
        claims.put(JwtTokenRule.AUTHORITIES_KEY.getValue(), user.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList()));
        return claims;
    }

}
