package kr.lunaticbum.back.lun.utils

import io.jsonwebtoken.*
import io.jsonwebtoken.security.Keys
import jakarta.servlet.http.Cookie
import kr.lunaticbum.back.lun.configs.JwtRule
import kr.lunaticbum.back.lun.configs.TokenStatus
import lombok.RequiredArgsConstructor
import lombok.extern.slf4j.Slf4j
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.nio.charset.StandardCharsets
import java.security.Key
import java.util.*


@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
class JwtUtil {

    fun getTokenStatus(token: String?, secretKey: Key?): TokenStatus {
        try {
            var cls = Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
            cls.body.keys.forEach {
                println("${it} >>> ${cls.body.get(it).toString()}")
            }
            return TokenStatus.AUTHENTICATED
        } catch (e: ExpiredJwtException) {
//            log.error(INVALID_EXPIRED_JWT.getMessage())
            return TokenStatus.EXPIRED
        } catch (e: IllegalArgumentException) {
//            log.error(INVALID_EXPIRED_JWT.getMessage())
            return TokenStatus.EXPIRED
        } catch (e: JwtException) {
            throw BusinessException(ErrorCode.INVALID_JWT)
        }
    }

    fun resolveTokenFromCookie(cookies: Array<Cookie>?, tokenPrefix: JwtRule): String {
        return Arrays.stream(cookies)
            .filter { cookie -> cookie.getName().equals(tokenPrefix.value, ignoreCase = true) }
            .findFirst()
            .map { it.value }
            .orElse("")
    }

    fun getSigningKey(secretKey: String): Key {
        val encodedKey = encodeToBase64(secretKey)
        return Keys.hmacShaKeyFor(encodedKey.toByteArray(StandardCharsets.UTF_8))
    }

    private fun encodeToBase64(secretKey: String): String {
        return Base64.getEncoder().encodeToString(secretKey.toByteArray())
    }

    fun resetToken(tokenPrefix: JwtRule): Cookie {
        val cookie: Cookie = Cookie(tokenPrefix.value, null)
        cookie.setMaxAge(0)
        cookie.setPath("/")
        return cookie
    }

    fun  extractToken(token: String?, secretKey: Key?): Jws<Claims>? {
        try {
            return Jwts.parserBuilder()
                .setSigningKey(secretKey)
                .build()
                .parseClaimsJws(token)
        } catch (e: JwtException) {
            throw BusinessException(ErrorCode.INVALID_JWT)
        }
    }
}
class BusinessException(error : ErrorCode) : Exception(error.name)

enum class ErrorCode {
    JWT_TOKEN_NOT_FOUND,
    NOT_AUTHENTICATED_USER,
    INVALID_EXPIRED_JWT,
    INVALID_JWT
}