## BaraDB Nim Client — Tests
import std/unittest
import std/strutils
import baradb/client

suite "QueryBuilder":
  test "Simple SELECT":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("name", "age").from("users").build()
    check sql == "SELECT name, age FROM users"

  test "SELECT with WHERE":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("name").from("users").where("age > 18").build()
    check sql == "SELECT name FROM users WHERE age > 18"

  test "SELECT with JOIN":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("u.name", "o.total").from("users u")
      .join("orders o", "u.id = o.user_id").build()
    check "JOIN" in sql
    check "ON" in sql

  test "SELECT with GROUP BY and HAVING":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("dept", "count(*)").from("employees")
      .groupBy("dept").having("count(*) > 5").build()
    check "GROUP BY" in sql
    check "HAVING" in sql

  test "SELECT with ORDER BY and LIMIT":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("name").from("users")
      .orderBy("name", "DESC").limit(10).offset(5).build()
    check "ORDER BY name DESC" in sql
    check "LIMIT 10" in sql
    check "OFFSET 5" in sql

  test "Full complex query":
    let client = newClient()
    let qb = newQueryBuilder(client)
    let sql = qb.select("u.name", "count(*) as cnt")
      .from("users u")
      .leftJoin("orders o", "u.id = o.user_id")
      .where("u.age > 18")
      .groupBy("u.name")
      .having("cnt > 3")
      .orderBy("cnt", "DESC")
      .limit(50)
      .build()
    check sql.startsWith("SELECT")
    check "LEFT JOIN" in sql
    check "WHERE" in sql
    check "GROUP BY" in sql
    check "HAVING" in sql
    check "ORDER BY" in sql
    check "LIMIT 50" in sql

suite "Client Config":
  test "Default config":
    let config = defaultConfig()
    check config.host == "127.0.0.1"
    check config.port == 9472
    check config.database == "default"

  test "Custom config":
    let config = ClientConfig(host: "db.example.com", port: 9999,
                              database: "production", username: "app")
    check config.host == "db.example.com"
    check config.port == 9999

  test "Client creation":
    let client = newClient()
    check not client.isConnected

suite "Wire Protocol":
  test "Build query message":
    let msg = makeQueryMessage(1, "SELECT 1")
    check msg.len > 0
    # First 4 bytes: mkQuery (0x02) in big-endian uint32
    check msg[3] == 2'u8

  test "Wire value serialization":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkString, strVal: "hello")
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkString
    check decoded.strVal == "hello"

  test "Null value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkNull)
    buf.serializeValue(val)
    check buf.len == 1
    check buf[0] == 0x00'u8

  test "Int64 value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkInt64, int64Val: 123456789)
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkInt64
    check decoded.int64Val == 123456789

  test "Array value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkArray, arrayVal: @[
      WireValue(kind: fkString, strVal: "a"),
      WireValue(kind: fkString, strVal: "b"),
    ])
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkArray
    check decoded.arrayVal.len == 2
    check decoded.arrayVal[0].strVal == "a"

suite "Client async":
  test "Client nextId":
    let client = newClient()
    check not client.isConnected


suite "Wire Protocol Extended":
  test "Int8 value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkInt8, int8Val: -42)
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkInt8
    check decoded.int8Val == -42

  test "Int16 value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkInt16, int16Val: -1000)
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkInt16
    check decoded.int16Val == -1000

  test "Float32 value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkFloat32, float32Val: 3.14'f32)
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkFloat32
    check decoded.float32Val == 3.14'f32

  test "Bytes value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkBytes, bytesVal: @[1'u8, 2'u8, 3'u8])
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkBytes
    check decoded.bytesVal == @[1'u8, 2'u8, 3'u8]

  test "Vector value roundtrip":
    var buf: seq[byte] = @[]
    let val = WireValue(kind: fkVector, vecVal: @[1.0'f32, 2.0'f32, 3.0'f32])
    buf.serializeValue(val)
    var pos = 0
    let decoded = buf.deserializeValue(pos)
    check decoded.kind == fkVector
    check decoded.vecVal.len == 3
    check decoded.vecVal[0] == 1.0'f32

  test "WireValue to string":
    check wireValueToString(WireValue(kind: fkNull)) == ""
    check wireValueToString(WireValue(kind: fkBool, boolVal: true)) == "true"
    check wireValueToString(WireValue(kind: fkInt32, int32Val: 42)) == "42"
    check wireValueToString(WireValue(kind: fkString, strVal: "hello")) == "hello"
    check wireValueToString(WireValue(kind: fkVector, vecVal: @[1.0'f32])) == "<vector:1>"

suite "Typed rows":
  test "QueryResult carries typed rows for string and int":
    let client = newClient()
    let qb = newQueryBuilder(client)
    discard qb
    check compiles(client.query("SELECT 1"))
