package wang.yaojia.webterm.wire

import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

/**
 * A4 boundary-validation primitives. Session-id vectors are the M7 UUID-v4 set from
 * `test/protocol.test.ts` / iOS `ServerVectorTests`; resize/cwd/type-whitelist mirror
 * `src/protocol.ts`.
 */
class ValidationTest {
    // ─── isValidSessionId (SESSION_ID_RE, M7) ────────────────────────────────────

    @Test
    fun `accepts valid uuid v4 including uppercase hex and variant 8`() {
        assertTrue(Validation.isValidSessionId("f47ac10b-58cc-4372-a567-0e02b2c3d479"))
        assertTrue(Validation.isValidSessionId("F47AC10B-58CC-4372-A567-0E02B2C3D479"))
        assertTrue(Validation.isValidSessionId("550e8400-e29b-41d4-8716-446655440000"))
    }

    @Test
    fun `rejects non-uuid empty wrong-version wrong-variant and malformed ids`() {
        val invalid = listOf(
            "abc123",
            "",
            "550e8400-e29b-11d4-a716-446655440000", // version nibble = 1 (v1)
            "f47ac10b-58cc-4372-c567-0e02b2c3d479", // variant 'c' (not 8/9/a/b)
            "f47ac10b58cc4372a5670e02b2c3d479", // no separators
            "f47ac10b-58cc-4372-a567-0e02b2c3d47", // one digit short
            "g47ac10b-58cc-4372-a567-0e02b2c3d479", // illegal char 'g'
        )
        for (candidate in invalid) {
            assertFalse(Validation.isValidSessionId(candidate), "should reject: $candidate")
        }
    }

    @Test
    fun `exposes the session-id regex for downstream reuse`() {
        assertTrue(Validation.SESSION_ID_RE.matches("f47ac10b-58cc-4372-a567-0e02b2c3d479"))
        assertFalse(Validation.SESSION_ID_RE.matches("nope"))
    }

    // ─── isValidResize ───────────────────────────────────────────────────────────

    @Test
    fun `accepts resize dimensions on the inclusive 1 to 1000 boundary`() {
        assertTrue(Validation.isValidResize(1, 1))
        assertTrue(Validation.isValidResize(1000, 1000))
        assertTrue(Validation.isValidResize(120, 40))
    }

    @Test
    fun `rejects resize dimensions outside 1 to 1000`() {
        assertFalse(Validation.isValidResize(0, 40))
        assertFalse(Validation.isValidResize(1001, 40))
        assertFalse(Validation.isValidResize(80, 0))
        assertFalse(Validation.isValidResize(80, 1001))
    }

    // ─── isAbsoluteCwd ───────────────────────────────────────────────────────────

    @Test
    fun `accepts absolute cwd and rejects relative or empty`() {
        assertTrue(Validation.isAbsoluteCwd("/a"))
        assertFalse(Validation.isAbsoluteCwd("a"))
        assertFalse(Validation.isAbsoluteCwd(""))
    }

    // ─── type whitelists ─────────────────────────────────────────────────────────

    @Test
    fun `whitelists exactly the client frame types`() {
        for (t in listOf("attach", "input", "resize", "approve", "reject")) {
            assertTrue(Validation.isAllowedClientType(t), "client type: $t")
        }
        for (t in listOf("blur", "ping", "attached", "")) {
            assertFalse(Validation.isAllowedClientType(t), "not a client type: $t")
        }
    }

    @Test
    fun `whitelists exactly the server frame types`() {
        for (t in listOf("attached", "output", "exit", "status", "telemetry")) {
            assertTrue(Validation.isAllowedServerType(t), "server type: $t")
        }
        for (t in listOf("input", "blur", "approve", "")) {
            assertFalse(Validation.isAllowedServerType(t), "not a server type: $t")
        }
    }
}
