package wang.yaojia.webterm.api.enroll

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Test
import java.math.BigInteger
import java.security.KeyPairGenerator
import java.security.interfaces.ECPublicKey
import java.security.spec.ECGenParameterSpec

/** B4 · The X9.63 uncompressed-point encoder — the security-load-bearing SubjectPublicKeyInfo bytes. */
class EcPointEncodingTest {
    @Test
    fun encodesAGeneratedP256KeyAs65UncompressedBytesRoundTrippingToTheCoordinates() {
        val kp = KeyPairGenerator.getInstance("EC").apply {
            initialize(ECGenParameterSpec("secp256r1"))
        }.generateKeyPair()
        val pub = kp.public as ECPublicKey

        val encoded = EcPointEncoding.x963(pub)

        assertEquals(65, encoded.size, "0x04 || X(32) || Y(32)")
        assertEquals(0x04, encoded[0].toInt() and 0xFF, "uncompressed-point prefix")
        // The 32-byte big-endian halves must be exactly the affine coordinates.
        val x = BigInteger(1, encoded.copyOfRange(1, 33))
        val y = BigInteger(1, encoded.copyOfRange(33, 65))
        assertEquals(pub.w.affineX, x)
        assertEquals(pub.w.affineY, y)
    }

    @Test
    fun leftPadsAShortCoordinateToTheFixedWidth() {
        // A small value must be left-padded with leading zeros to exactly 32 bytes.
        val padded = EcPointEncoding.toFixedLengthUnsigned(BigInteger.valueOf(1), 32)
        assertEquals(32, padded.size)
        assertEquals(1, padded[31].toInt())
        assertEquals(0, padded[0].toInt())
    }

    @Test
    fun dropsTheBigIntegerSignByteWhenPresent() {
        // A value whose top bit is set carries a leading 0x00 sign byte in BigInteger.toByteArray();
        // it must be dropped, not counted toward the width.
        val highBit = BigInteger(1, ByteArray(32) { 0xFF.toByte() })
        val encoded = EcPointEncoding.toFixedLengthUnsigned(highBit, 32)
        assertEquals(32, encoded.size)
        assertEquals(0xFF, encoded[0].toInt() and 0xFF)
    }

    @Test
    fun rejectsACoordinateThatDoesNotFit() {
        val tooBig = BigInteger.ONE.shiftLeft(256) // needs 33 bytes
        assertThrows(IllegalArgumentException::class.java) {
            EcPointEncoding.toFixedLengthUnsigned(tooBig, 32)
        }
    }
}
