## KDL Parser Integration for NIP
## Provides KDL parsing functionality for NIP configuration and package files

import std/[tables, options]
import kdl

# Re-export main types
export kdl.KdlVal, kdl.KdlNode, kdl.KdlDoc, kdl.KValKind, kdl.KdlError

# Helper procs for easier value access
proc getString*(val: KdlVal): string =
  ## Get string value, raises if not a string
  if val.kind != KString:
    raise newException(ValueError, "KdlVal is not a string")
  val.str

proc getInt*(val: KdlVal): int64 =
  ## Get int value, raises if not an int
  if val.kind != KInt:
    raise newException(ValueError, "KdlVal is not an int")
  val.num

proc getFloat*(val: KdlVal): float64 =
  ## Get float value, raises if not a float
  if val.kind != KFloat:
    raise newException(ValueError, "KdlVal is not a float")
  val.fnum

proc getBool*(val: KdlVal): bool =
  ## Get bool value, raises if not a bool
  if val.kind != KBool:
    raise newException(ValueError, "KdlVal is not a bool")
  val.boolean

proc isString*(val: KdlVal): bool =
  ## Check if value is a string
  val.kind == KString

proc isInt*(val: KdlVal): bool =
  ## Check if value is an int
  val.kind == KInt

proc isFloat*(val: KdlVal): bool =
  ## Check if value is a float
  val.kind == KFloat

proc isBool*(val: KdlVal): bool =
  ## Check if value is a bool
  val.kind == KBool

proc isNull*(val: KdlVal): bool =
  ## Check if value is null
  val.kind == KNull

# Helper procs for node access
proc getArg*(node: KdlNode, idx: int): KdlVal =
  ## Get argument by index
  if idx < 0 or idx >= node.args.len:
    raise newException(IndexDefect, "Argument index out of bounds")
  node.args[idx]

proc getArgString*(node: KdlNode, idx: int): string =
  ## Get string argument by index
  node.getArg(idx).getString()

proc getArgInt*(node: KdlNode, idx: int): int64 =
  ## Get int argument by index
  node.getArg(idx).getInt()

proc getArgBool*(node: KdlNode, idx: int): bool =
  ## Get bool argument by index
  node.getArg(idx).getBool()

proc getProp*(node: KdlNode, key: string): KdlVal =
  ## Get property by key
  if not node.props.hasKey(key):
    raise newException(KeyError, "Property not found: " & key)
  node.props[key]

proc getPropString*(node: KdlNode, key: string): string =
  ## Get string property by key
  node.getProp(key).getString()

proc getPropInt*(node: KdlNode, key: string): int64 =
  ## Get int property by key
  node.getProp(key).getInt()

proc getPropBool*(node: KdlNode, key: string): bool =
  ## Get bool property by key
  node.getProp(key).getBool()

proc getPropString*(node: KdlNode, key: string, default: string): string =
  ## Get string property by key with default
  if node.props.hasKey(key):
    node.props[key].getString()
  else:
    default

proc getPropInt*(node: KdlNode, key: string, default: int64): int64 =
  ## Get int property by key with default
  if node.props.hasKey(key):
    node.props[key].getInt()
  else:
    default

proc getPropBool*(node: KdlNode, key: string, default: bool): bool =
  ## Get bool property by key with default
  if node.props.hasKey(key):
    node.props[key].getBool()
  else:
    default

proc hasProp*(node: KdlNode, key: string): bool =
  ## Check if node has a property
  node.props.hasKey(key)

proc findChild*(node: KdlNode, name: string): Option[KdlNode] =
  ## Find first child node by name
  for child in node.children:
    if child.name == name:
      return some(child)
  none(KdlNode)

proc findChildren*(node: KdlNode, name: string): seq[KdlNode] =
  ## Find all child nodes by name
  result = @[]
  for child in node.children:
    if child.name == name:
      result.add(child)

# Document helpers
proc findNode*(doc: KdlDoc, name: string): Option[KdlNode] =
  ## Find first top-level node by name
  for node in doc:
    if node.name == name:
      return some(node)
  none(KdlNode)

proc findNodes*(doc: KdlDoc, name: string): seq[KdlNode] =
  ## Find all top-level nodes by name
  result = @[]
  for node in doc:
    if node.name == name:
      result.add(node)

# Parsing functions
proc parseKdlString*(content: string): KdlDoc =
  ## Parse KDL from string
  try:
    result = kdl.parseKdl(content)
  except KdlError as e:
    raise newException(ValueError, "KDL parse error: " & e.msg)

proc parseKdlFile*(path: string): KdlDoc =
  ## Parse KDL from file
  try:
    result = kdl.parseKdlFile(path)
  except KdlError as e:
    raise newException(ValueError, "KDL parse error in file " & path & ": " & e.msg)
  except IOError as e:
    raise newException(IOError, "Failed to read file " & path & ": " & e.msg)

# Serialization
proc toKdlString*(doc: KdlDoc): string =
  ## Convert KdlDoc to string
  $doc

proc toKdlString*(node: KdlNode): string =
  ## Convert KdlNode to string
  $node
