{.deprecated: "Use the canonical baradb/client from clients/nim instead.".}
## BaraDB Client — Nim client library
import std/asyncdispatch
import std/asyncnet
import std/net as netmod
import std/locks
import std/strutils
import ../protocol/wire

type
  ClientConfig* = object
    host*: string
    port*: int
    database*: string
    username*: string
    password*: string
    timeout*: int  # milliseconds
    maxRetries*: int

  QueryResult* = object
    columns*: seq[string]
    rows*: seq[seq[string]]
    rowCount*: int
    affectedRows*: int
    executionTime*: float64  # seconds

  BaraClient* = ref object
    config: ClientConfig
    socket: AsyncSocket
    connected: bool
    requestId: uint32

proc defaultClientConfig*(): ClientConfig =
  ClientConfig(
    host: "127.0.0.1",
    port: 9472,
    database: "default",
    username: "admin",
    password: "",
    timeout: 30000,
    maxRetries: 3,
  )

proc newBaraClient*(config: ClientConfig = defaultClientConfig()): BaraClient =
  BaraClient(
    config: config,
    socket: newAsyncSocket(),
    connected: false,
    requestId: 0,
  )

proc connect*(client: BaraClient) {.async.} =
  await client.socket.connect(client.config.host, Port(client.config.port))
  client.connected = true

proc disconnect*(client: BaraClient) {.async.} =
  if client.connected:
    client.socket.close()
    client.connected = false

proc nextRequestId(client: BaraClient): uint32 =
  inc client.requestId
  return client.requestId

proc query*(client: BaraClient, sql: string): Future[QueryResult] {.async.} =
  if not client.connected:
    raise newException(IOError, "Not connected")

  let reqId = client.nextRequestId()
  let msg = makeQueryMessage(reqId, sql)
  await client.socket.send(cast[string](msg))

  # Read response
  let response = await client.socket.recv(8192)
  if response.len == 0:
    raise newException(IOError, "Connection closed by server")

  result = QueryResult(columns: @[], rows: @[], rowCount: 0, affectedRows: 0)

  # Parse response (simplified)
  if response.len > 0:
    result.rowCount = 0

proc execute*(client: BaraClient, sql: string): Future[int] {.async.} =
  let qr = await client.query(sql)
  return qr.affectedRows

proc isConnected*(client: BaraClient): bool = client.connected

# Synchronous wrapper (blocking socket, thread-safe)
type
  SyncClient* = ref object
    config: ClientConfig
    socket: netmod.Socket
    connected: bool
    requestId: uint32
    lock: Lock

proc newSyncClient*(config: ClientConfig = defaultClientConfig()): SyncClient =
  result = SyncClient(config: config, connected: false, requestId: 0)
  result.socket = netmod.newSocket()
  initLock(result.lock)

proc connect*(client: SyncClient) =
  netmod.connect(client.socket, client.config.host, Port(client.config.port))
  client.connected = true

proc disconnect*(client: SyncClient) =
  if client.connected:
    try:
      let msg = makeQueryMessage(0, "DISCONNECT")
      netmod.send(client.socket, cast[string](msg))
    except IOError, OSError: discard
    netmod.close(client.socket)
    client.connected = false
    deinitLock(client.lock)

proc query*(client: SyncClient, sql: string): QueryResult =
  acquire(client.lock)
  try:
    if not client.connected:
      raise newException(IOError, "Not connected")
    let reqId = client.requestId + 1
    client.requestId = reqId
    let msg = makeQueryMessage(reqId, sql)
    netmod.send(client.socket, cast[string](msg))
    # Simplified response read — read until we get something
    var response = ""
    while response.len == 0:
      response = netmod.recv(client.socket, 8192)
      if response.len == 0:
        raise newException(IOError, "Connection closed by server")
    result = QueryResult(columns: @[], rows: @[], rowCount: 0, affectedRows: 0)
  finally:
    release(client.lock)

proc execute*(client: SyncClient, sql: string): int =
  let qr = client.query(sql)
  return qr.affectedRows

proc isConnected*(client: SyncClient): bool =
  client.connected

# Connection string parser
proc parseConnectionString*(connStr: string): ClientConfig =
  result = defaultClientConfig()
  let parts = connStr.split(" ")
  for part in parts:
    let kv = part.split("=", 1)
    if kv.len == 2:
      case kv[0].toLower()
      of "host": result.host = kv[1]
      of "port": result.port = parseInt(kv[1])
      of "database", "dbname": result.database = kv[1]
      of "user", "username": result.username = kv[1]
      of "password", "pass": result.password = kv[1]
      of "connect_timeout", "timeout": result.timeout = parseInt(kv[1])
      else: discard

# Fluent query builder
type
  QueryBuilder* = ref object
    client: BaraClient
    selectCols: seq[string]
    fromTable: string
    whereClauses: seq[string]
    orderByCols: seq[string]
    orderDirs: seq[string]
    limitVal: int
    offsetVal: int
    groupByCols: seq[string]
    havingClause: string
    joinClauses: seq[string]

proc newQueryBuilder*(client: BaraClient): QueryBuilder =
  QueryBuilder(
    client: client,
    selectCols: @[],
    fromTable: "",
    whereClauses: @[],
    orderByCols: @[],
    orderDirs: @[],
    limitVal: 0,
    offsetVal: 0,
    groupByCols: @[],
    havingClause: "",
    joinClauses: @[],
  )

proc select*(qb: QueryBuilder, cols: varargs[string]): QueryBuilder =
  for col in cols:
    qb.selectCols.add(col)
  return qb

proc `from`*(qb: QueryBuilder, table: string): QueryBuilder =
  qb.fromTable = table
  return qb

proc where*(qb: QueryBuilder, clause: string): QueryBuilder =
  qb.whereClauses.add(clause)
  return qb

proc orderBy*(qb: QueryBuilder, col: string, dir: string = "ASC"): QueryBuilder =
  qb.orderByCols.add(col)
  qb.orderDirs.add(dir)
  return qb

proc limit*(qb: QueryBuilder, n: int): QueryBuilder =
  qb.limitVal = n
  return qb

proc offset*(qb: QueryBuilder, n: int): QueryBuilder =
  qb.offsetVal = n
  return qb

proc join*(qb: QueryBuilder, table: string, on: string): QueryBuilder =
  qb.joinClauses.add("JOIN " & table & " ON " & on)
  return qb

proc groupBy*(qb: QueryBuilder, cols: varargs[string]): QueryBuilder =
  for col in cols:
    qb.groupByCols.add(col)
  return qb

proc having*(qb: QueryBuilder, clause: string): QueryBuilder =
  qb.havingClause = clause
  return qb

proc build*(qb: QueryBuilder): string =
  result = "SELECT "
  if qb.selectCols.len == 0:
    result &= "*"
  else:
    result &= qb.selectCols.join(", ")
  result &= " FROM " & qb.fromTable
  for joinClause in qb.joinClauses:
    result &= " " & joinClause
  if qb.whereClauses.len > 0:
    result &= " WHERE " & qb.whereClauses.join(" AND ")
  if qb.groupByCols.len > 0:
    result &= " GROUP BY " & qb.groupByCols.join(", ")
  if qb.havingClause.len > 0:
    result &= " HAVING " & qb.havingClause
  if qb.orderByCols.len > 0:
    result &= " ORDER BY "
    for i, col in qb.orderByCols:
      if i > 0: result &= ", "
      result &= col & " " & qb.orderDirs[i]
  if qb.limitVal > 0:
    result &= " LIMIT " & $qb.limitVal
  if qb.offsetVal > 0:
    result &= " OFFSET " & $qb.offsetVal

proc exec*(qb: QueryBuilder): Future[QueryResult] {.async.} =
  return await qb.client.query(qb.build())
