package com.spring.infra.security.jwt;

import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Arrays;
import java.util.Base64;

import javax.servlet.http.Cookie;

import org.springframework.stereotype.Component;

import com.spring.infra.security.error.SecurityExceptionRule;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;

/**
 * JWT 토큰 관련 유틸리티 기능을 제공하는 클래스입니다.
 * 
 * <p>이 클래스는 JWT 토큰의 상태 확인, 쿠키에서 토큰 추출, 서명 키 생성 등의 기능을 제공합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
public class JwtTokenUtil {

    /**
     * JWT 토큰의 상태를 확인합니다.
     * 
     * @param token 검증할 JWT 토큰
     * @param secretKey 토큰 검증에 사용할 비밀 키
     * @return 토큰의 상태 (AUTHENTICATED, EXPIRED, INVALID)
     */
    public JwtTokenStatus getTokenStatus(String token, Key secretKey) {
        try {
            Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token);
            return JwtTokenStatus.AUTHENTICATED;
        } catch (ExpiredJwtException | IllegalArgumentException e) {
            return JwtTokenStatus.EXPIRED;
        } catch (JwtException e) {
            return JwtTokenStatus.INVALID;
        }
    }

    /**
     * JWT 토큰의 상태를 확인하고 예외를 던집니다.
     * 
     * @param token 검증할 JWT 토큰
     * @param secretKey 토큰 검증에 사용할 비밀 키
     */
    public void tokenStatus(String token, Key secretKey) {
        try {
            Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token);
        } catch (ExpiredJwtException | IllegalArgumentException e) {
            throw new ExpiredJwtException(null, null, SecurityExceptionRule.EXPIRED_JWT_ERROR.getMessage());
        } catch (SignatureException e) {
            throw new SignatureException(SecurityExceptionRule.SIGNATURE_ERROR.getMessage());
        } catch (MalformedJwtException e) {
            throw new MalformedJwtException(SecurityExceptionRule.MALFORMED_JWT_ERROR.getMessage());
        } catch (JwtException e) {
            throw new JwtException(SecurityExceptionRule.JWT_TOKEN_ERROR.getMessage());
        }
    }

    /**
     * 쿠키에서 특정 접두사를 가진 토큰을 추출합니다.
     * 
     * @param cookies 쿠키 배열
     * @param tokenPrefix 토큰 접두사
     * @return 추출된 토큰 값 (없으면 빈 문자열)
     */
    public String resolveTokenFromCookie(Cookie[] cookies, JwtTokenRule tokenPrefix) {
        return Arrays.stream(cookies)
                .filter(cookie -> cookie.getName().equals(tokenPrefix.getValue()))
                .findFirst()
                .map(Cookie::getValue)
                .orElse("");
    }

    /**
     * 주어진 비밀 키로 서명 키를 생성합니다.
     * 
     * @param secretKey 비밀 키 문자열
     * @return 생성된 서명 키
     */
    public Key getSigningKey(String secretKey) {
        String encodedKey = encodeToBase64(secretKey);
        return Keys.hmacShaKeyFor(encodedKey.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 문자열을 Base64로 인코딩합니다.
     * 
     * @param secretKey 인코딩할 문자열
     * @return Base64로 인코딩된 문자열
     */
    private String encodeToBase64(String secretKey) {
        return Base64.getEncoder().encodeToString(secretKey.getBytes());
    }

    /**
     * 토큰을 리셋하는 쿠키를 생성합니다.
     * 
     * @param tokenPrefix 토큰 접두사
     * @return 생성된 쿠키 객체
     */
    public Cookie resetToken(JwtTokenRule tokenPrefix) {
        Cookie cookie = new Cookie(tokenPrefix.getValue(), null);
        cookie.setMaxAge(0);
        cookie.setPath("/");
        return cookie;
    }

}
