package com.swoosh.microblog.data.api

import io.jsonwebtoken.Jwts
import org.junit.Assert.*
import org.junit.Test
import java.util.Base64
import javax.crypto.spec.SecretKeySpec

class GhostJwtGeneratorTest {

    // A valid test key: 16-char hex ID : 64-char hex secret
    private val validKeyId = "abcdef1234567890"
    private val validSecret = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
    private val validApiKey = "$validKeyId:$validSecret"

    @Test
    fun `generateToken produces valid JWT with correct header`() {
        val token = GhostJwtGenerator.generateToken(validApiKey)

        // JWT has three parts separated by dots
        val parts = token.split(".")
        assertEquals("JWT should have 3 parts", 3, parts.size)

        // Decode header and verify kid
        val headerJson = String(Base64.getUrlDecoder().decode(parts[0]))
        assertTrue("Header should contain kid", headerJson.contains("\"kid\""))
        assertTrue("Header should contain correct key ID", headerJson.contains(validKeyId))
        assertTrue("Header should specify HS256", headerJson.contains("HS256"))
    }

    @Test
    fun `generateToken creates JWT with correct claims`() {
        val beforeGeneration = System.currentTimeMillis() / 1000
        val token = GhostJwtGenerator.generateToken(validApiKey)
        val afterGeneration = System.currentTimeMillis() / 1000

        // Verify by parsing the token with the same key
        val secretBytes = validSecret.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
        val key = SecretKeySpec(secretBytes, "HmacSHA256")

        val claims = Jwts.parser()
            .verifyWith(key)
            .build()
            .parseSignedClaims(token)

        // Check audience
        val audience = claims.payload.audience
        assertTrue("Audience should contain /admin/", audience.contains("/admin/"))

        // Check issued at
        val iat = claims.payload.issuedAt.time / 1000
        assertTrue("iat should be around now", iat >= beforeGeneration - 1 && iat <= afterGeneration + 1)

        // Check expiration (5 minutes from iat)
        val exp = claims.payload.expiration.time / 1000
        val expectedExp = iat + 300
        assertTrue("exp should be ~5 minutes after iat", exp >= expectedExp - 1 && exp <= expectedExp + 1)
    }

    @Test
    fun `generateToken with different keys produces different tokens`() {
        val key1 = "aaaa1234567890bb:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
        val key2 = "cccc1234567890dd:b1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"

        val token1 = GhostJwtGenerator.generateToken(key1)
        val token2 = GhostJwtGenerator.generateToken(key2)

        assertNotEquals("Different keys should produce different tokens", token1, token2)
    }

    @Test(expected = IllegalArgumentException::class)
    fun `generateToken throws on missing colon`() {
        GhostJwtGenerator.generateToken("invalidkeyformat")
    }

    @Test(expected = IllegalArgumentException::class)
    fun `generateToken throws on empty key`() {
        GhostJwtGenerator.generateToken("")
    }

    @Test(expected = IllegalArgumentException::class)
    fun `generateToken throws on multiple colons`() {
        GhostJwtGenerator.generateToken("a:b:c")
    }

    @Test
    fun `generateToken correctly decodes hex secret`() {
        // Use a known hex value to verify decoding
        val knownHex = "48656c6c6f" // "Hello" in hex
        val key = "testid:${knownHex}00000000000000000000000000000000000000000000000000000000"
        // Should not throw - proves hex decoding works
        val token = GhostJwtGenerator.generateToken(key)
        assertNotNull(token)
        assertTrue(token.isNotEmpty())
    }

    @Test
    fun `generateToken header contains kid matching key id`() {
        val keyId = "my_custom_key_id"
        val secret = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
        val token = GhostJwtGenerator.generateToken("$keyId:$secret")

        val headerJson = String(Base64.getUrlDecoder().decode(token.split(".")[0]))
        assertTrue("Header should contain the exact key ID", headerJson.contains(keyId))
    }

    @Test(expected = NumberFormatException::class)
    fun `generateToken throws on invalid hex in secret`() {
        GhostJwtGenerator.generateToken("keyid:ZZZZ")
    }
}
