# SPDX-License-Identifier: LUL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus SDK.
# See legal/LICENSE_UNBOUND.md for license terms.

# src/npl/nipbox/nipbox.nim
# Phase 21: The Teleporter - Networked Object Pipelines

import strutils, parseutils, tables, sequtils, json
import kdl
import ../../libs/membrane/libc as lb
import ../../libs/membrane/libc_net as lnet
import ../../libs/membrane/fs/sfs_user as sfs
import editor
import ../../libs/membrane/term # Phase 26: Visual Cortex

# Phase 30: Pledge Constants
const
  PLEDGE_STDIO* = 0x0001'u64
  PLEDGE_RPATH* = 0x0002'u64
  PLEDGE_WPATH* = 0x0004'u64
  PLEDGE_INET* = 0x0008'u64
  PLEDGE_EXEC* = 0x0010'u64
  PLEDGE_ALL* = 0xFFFFFFFFFFFFFFFF'u64

  # Phase 34: Phoenix Version Marker
  NIPBOX_VERSION* = "v0.8.8-PHOENIX"

type
  PipelineData = seq[Node]

# --- ENVIRONMENT ---
var env_table = initTable[string, string]()
var last_exit_code: int = 0

# --- HELPERS ---


var use_logfile = false


proc print(s: string) =
  lb.print(s)




proc expand_vars(text: string): string =
  # Replace $var with env value, including special $? for exit code
  result = ""
  var i = 0
  while i < text.len:
    if text[i] == '$':
      # Extract var name
      var varname = ""
      var j = i + 1
      if j < text.len and text[j] == '?':
        varname = "?"
        j += 1
      else:
        while j < text.len and (text[j].isAlphaNumeric() or text[j] == '_'):
          varname.add(text[j])
          j += 1

      if varname.len > 0:
        if varname == "?":
          result.add($last_exit_code)
        elif env_table.hasKey(varname):
          result.add(env_table[varname])
        else:
          result.add("$" & varname) # Leave unexpanded if not found
        i = j
      else:
        result.add('$')
        i += 1
    else:
      result.add(text[i])
      i += 1

proc render_output(data: PipelineData) =
  if data.len == 0: return

  let typeName = if data.len > 0: data[0].name.toUpperAscii() else: "VOID"
  print("\n\x1b[1;36mTYPE: " & typeName & "\x1b[0m\n")
  print(repeat("-", 40) & "\n")

  for node in data:
    var line = "  "
    for p in node.props:
      line.add(p.key & ":" & $p.val & "  ")
    for arg in node.args:
      line.add($arg & " ")
    # Truncate content for display if too long
    if line.len > 80: line = line[0..77] & "..."
    print(line & "\n")

  print(repeat("-", 40) & "\n")
  print("Total: " & $data.len & " objects.\n\n")

# --- PHASE 30: WORKER SYSTEM ---

type
  WorkerPacket = ref object
    command_fn: proc(args: seq[string], input: PipelineData): PipelineData
    args: seq[string]
    input: PipelineData
    output: PipelineData
    exit_code: int
    pledge_mask: uint64

# Worker trampoline (C-compatible)
proc dispatch_worker(arg: uint64) {.cdecl.} =
  let packet = cast[ptr WorkerPacket](arg)
  if packet == nil: return

  # Apply pledge
  if packet.pledge_mask != PLEDGE_ALL:
    discard lb.pledge(packet.pledge_mask)

  # Execute command
  try:
    packet.output = packet.command_fn(packet.args, packet.input)
    packet.exit_code = 0
  except:
    packet.output = @[]
    packet.exit_code = 1

# Helper to spawn command as worker
proc spawn_command(cmd_fn: proc(args: seq[string], input: PipelineData): PipelineData,
                   args: seq[string], input: PipelineData,
                       pledge: uint64): PipelineData =
  var packet = WorkerPacket(
    command_fn: cmd_fn,
    args: args,
    input: input,
    output: @[],
    exit_code: 0,
    pledge_mask: pledge
  )

  let packet_ptr = cast[uint64](cast[pointer](packet))
  let fid = lb.spawn(dispatch_worker, packet_ptr)

  if fid < 0:
    # Spawn failed, run inline
    return cmd_fn(args, input)

  discard lb.join(fid)
  return packet.output



proc cmd_crash*(args: seq[string], input: PipelineData): PipelineData =
  print("[NipBox] PREPARING TO CRASH...\n")

  # Crash Logic: Null Pointer Dereference in Worker
  let worker_crash = proc(args: seq[string],
      input: PipelineData): PipelineData =
    print("[Worker] Goodbye, cruel world!\n")
    var ptr_null = cast[ptr int](0)
    ptr_null[] = 42 # PAGE FAULT
    return @[]

  # Spawn the suicider
  return spawn_command(worker_crash, args, input, PLEDGE_ALL)

proc cmd_upgrade*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 1:
    print("Usage: sys.upgrade <path>\n")
    return @[]

  let path = args[0]
  print("[NipBox] Initiating Phoenix Protocol for Self...\n")
  print("[NipBox] Target: " & path & "\n")

  # Upgrade Self (Subject runs as ID 3 usually)
  let res = lb.upgrade(3, path.cstring)
  if res < 0:
    print("Error: Upgrade failed (" & $res & ")\n")

  # Does not return if success.
  return @[]

# --- COMMANDS ---

proc cmd_ls*(args: seq[string], input: PipelineData): PipelineData =
  result = @[]
  let files = lb.get_vfs_listing()
  for f in files:
    let node = newNode("file")
    node.addArg(newVal(f))
    node.addProp("name", newVal(f))
    if f.endsWith(".nsh"):
      node.addProp("type", newVal("script"))
      node.addProp("size", newVal(335))
    elif f.contains("nipbox"):
      node.addProp("type", newVal("binary"))
      node.addProp("size", newVal(800000))
    else:
      node.addProp("type", newVal("unknown"))
      node.addProp("size", newVal(100))
    result.add(node)

proc cmd_mount*(args: seq[string], input: PipelineData): PipelineData =
  print("[mount] System Disk Engaged.\n")
  return @[]

proc cmd_cp*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 2:
    print("Usage: cp <source> <dest>\n")
    return @[]

  let src = args[0]
  let dest = args[1]

  let fd_src = lb.open(src.cstring, 0) # O_RDONLY
  if fd_src < 0:
    print("cp: cannot stat '" & src & "': No such file\n")
    return @[]

  # O_WRONLY(1) | O_CREAT(64) | O_TRUNC(512) = 577
  let fd_dest = lb.open(dest.cstring, 577) 
  if fd_dest < 0:
    print("cp: cannot create '" & dest & "'\n")
    discard lb.close(fd_src)
    return @[]

  var buf: array[4096, char]
  var total = 0
  while true:
    let n = lb.read(fd_src, addr buf[0], 4096)
    if n <= 0: break
    let written = lb.write(fd_dest, addr buf[0], csize_t(n))
    if written < 0:
      print("cp: write error\n")
      break
    total += int(written)

  discard lb.close(fd_src)
  discard lb.close(fd_dest)
  print("[OK] Copied " & $total & " bytes.\n")
  return @[]

proc cmd_mv*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 2:
    print("Usage: mv <source> <dest>\n")
    return @[]
  
  # Step 1: Copy
  print("[mv] Copying...\n")
  discard cmd_cp(args, input)
  
  # Step 2: Unlink (Not yet supported by Kernel)
  print("[mv] Warning: Original file '" & args[0] & "' retained (SFS unlink not implemented).\n")
  return @[]

proc cmd_touch*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 1:
      print("Usage: touch <filename>\n")
      return @[]
  
  let fd = lb.open(args[0].cstring, 577) # O_CREAT | O_TRUNC
  if fd >= 0:
      discard lb.close(fd)
  else:
      print("touch: cannot touch '" & args[0] & "'\n")
  return @[]

proc cmd_matrix*(args: seq[string], input: PipelineData): PipelineData =
  let state = if args.len > 0: args[0].toUpperAscii() else: "STATUS: NOMINAL"
  print("[matrix] " & state & "\n")
  return @[]

proc cmd_cat*(args: seq[string], input: PipelineData): PipelineData =
  if args.len == 0: return @[]
  let fd = lb.open(args[0].cstring, 0)
  if fd < 0:
    print("Error: Could not open " & args[0] & "\n")
    return @[]
  var buf: array[1024, char]
  while true:
    let n = lb.read(fd, addr buf[0], 1024)
    if n <= 0: break
    discard lb.write(cint(1), addr buf[0], csize_t(n))
  discard lb.close(fd)
  print("\n")
  return @[]

proc cmd_write*(args: seq[string], input: PipelineData): PipelineData =
  ## write <filename> <content>
  ## Uses USERLAND SFS (Block Valve architecture)
  if args.len < 2:
    print("Usage: write <filename> <content>\n")
    return @[]
  
  let filename = args[0]
  let content = args[1..^1].join(" ")
  
  # Mount userland FS if not already done
  if not sfs.sfs_is_mounted():
    discard sfs.sfs_mount()
  
  let bytes_written = sfs.sfs_write(filename, cast[pointer](unsafeAddr content[0]), content.len)
  if bytes_written > 0:
    print("[Glass Vault] Written " & $bytes_written & " bytes to: " & filename & " (Userland SFS)\n")
  else:
    print("Error: Could not write to " & filename & "\n")
  return @[]

proc cmd_read*(args: seq[string], input: PipelineData): PipelineData =
  ## read <filename>
  ## Uses USERLAND SFS (Block Valve architecture)
  if args.len == 0:
    print("Usage: read <filename>\n")
    return @[]
  
  let filename = args[0]
  
  # Mount userland FS if not already done
  if not sfs.sfs_is_mounted():
    discard sfs.sfs_mount()
  
  var buf: array[4096, char]
  let bytes_read = sfs.sfs_read(filename, addr buf[0], 4096)
  if bytes_read > 0:
    discard lb.write(cint(1), addr buf[0], csize_t(bytes_read))
    print("\n[Glass Vault] Read " & $bytes_read & " bytes from: " & filename & " (Userland SFS)\n")
  else:
    print("Error: Could not open " & filename & "\n")
  return @[]

proc cmd_edit*(args: seq[string], input: PipelineData): PipelineData =
  if args.len == 0:
    print("Usage: edit <filename>\n")
    return @[]
  start_editor(args[0])
  return @[]

proc cmd_echo*(args: seq[string], input: PipelineData): PipelineData =
  let msg = args.join(" ")
  if input.len == 0:
    print(msg & "\n")

  let node = newNode("text")
  node.addArg(newVal(msg))
  node.addProp("content", newVal(msg))
  return @[node]

proc cmd_where*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 3:
    print("Usage: where <key> <op> <val>\n")
    return input

  let key = args[0]
  let op = args[1]
  let targetValStr = args[2]

  var targetVal = 0
  var isInt = parseInt(targetValStr, targetVal) > 0

  result = @[]
  for node in input:
    var found = false
    var nodeValInt = 0
    var nodeValStr = ""

    for p in node.props:
      if p.key == key:
        if p.val.kind == VInt:
          nodeValInt = p.val.i
          found = true
        elif p.val.kind == VString:
          nodeValStr = p.val.s
          discard parseInt(nodeValStr, nodeValInt)
          found = true
        break

    if found:
      let match = case op:
        of ">": nodeValInt > targetVal
        of "<": nodeValInt < targetVal
        of "==": (if not isInt: nodeValStr == targetValStr else: nodeValInt == targetVal)
        else: false
      if match: result.add(node)

# --- PHASE 21: THE TELEPORTER ---

proc cmd_http_get*(args: seq[string], input: PipelineData): PipelineData =
  if args.len == 0:
    print("Usage: http.get <ip:port>\n")
    return @[]

  let target = args[0]
  let parts = target.split(':')
  if parts.len != 2:
    print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
    return @[]

  let ip_str = parts[0]
  let port = uint16(parseInt(parts[1]))

  # Parse IP (A.B.C.D)
  let ip_parts = ip_str.split('.')
  if ip_parts.len != 4: return @[]

  # LwIP IP encoding (Network Byte Order for internal, but our pack uses uint32)
  # Actually net_glue.nim uses the same pack logic.
  let ip_val = (uint32(parseInt(ip_parts[0])) shl 0) or
               (uint32(parseInt(ip_parts[1])) shl 8) or
               (uint32(parseInt(ip_parts[2])) shl 16) or
               (uint32(parseInt(ip_parts[3])) shl 24)

  print("[Teleporter] Connecting to " & target & "...\n")
  let fd = lb.socket(2, 1, 0) # AF_INET=2, SOCK_STREAM=1
  if fd < 100: return @[]

  # Construct SockAddrIn
  type SockAddrIn = object
    sin_family: uint16
    sin_port: uint16
    sin_addr: uint32
    sin_zero: array[8, char]

  var addr_in: SockAddrIn
  addr_in.sin_family = 2
  # htons for port (8000 -> 0x401F -> 0x1F40? No, manual)
  addr_in.sin_port = ((port and 0xFF) shl 8) or (port shr 8)
  addr_in.sin_addr = ip_val

  if lb.connect(fd, addr addr_in, sizeof(addr_in)) < 0:
    print("Error: Handshake FAILED.\n")
    return @[]

  # Wait for establishment (pumping the stack)
  var timeout = 0
  while timeout < 1000:
    # lb.pump_membrane_stack() - Handled by Kernel
    # Check if connected (we need a way to check socket state)
    # For now, let's assume if we can send, we are connected or it will buffer.
    # In our net_glue, glue_write returns -1 if not established.
    let test_req = "GET / HTTP/1.1\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
    let n = lb.send(cint(fd), cast[pointer](unsafeAddr test_req[0]), csize_t(
        test_req.len), 0)
    if n > 0: break
    timeout += 1
    # Busy wait a bit
    for i in 0..1000: discard

  if timeout >= 1000:
    print("Error: Connection TIMEOUT.\n")
    discard lb.close(cint(fd))
    return @[]

  print("[Teleporter] Request Sent. Waiting for response...\n")

  var response_body = ""
  var buf: array[2048, char]
  timeout = 0
  while timeout < 5000:
    # lb.pump_membrane_stack()
    let n = lb.recv(cint(fd), addr buf[0], 2048, 0)
    if n > 0:
      for i in 0..<n: response_body.add(buf[i])
      timeout = 0 # Reset timeout on data
    elif n == 0:
      # EOF
      break
    else:
      # EAGAIN
      timeout += 1
      for i in 0..1000: discard

  discard lb.close(cint(fd))
  print("[Teleporter] Received " & $response_body.len & " bytes.\n")

  let node = newNode("response")
  node.addProp("status", newVal(200)) # Simple shim
  node.addProp("size", newVal(response_body.len))
  node.addProp("body", newVal(response_body))
  return @[node]

proc cmd_http_download*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 2:
    print("Usage: http.download <ip:port/path> <outfile>\n")
    return @[]

  let url_arg = args[0]
  let outfile = args[1]

  var host_part = url_arg
  var path_str = "/"

  let slash_pos = url_arg.find('/')
  if slash_pos != -1:
    host_part = url_arg[0..<slash_pos]
    path_str = url_arg[slash_pos..^1]

  let parts = host_part.split(':')
  if parts.len != 2:
    print("Error: Target must be IP:PORT (e.g. 10.0.2.2:8000)\n")
    return @[]

  let ip_str = parts[0]
  let port = uint16(parseInt(parts[1]))

  # Parse IP
  var ip_val: uint32 = 0
  try:
    let p = ip_str.split('.')
    ip_val = (uint32(parseInt(p[0])) and 0xFF) or
             ((uint32(parseInt(p[1])) and 0xFF) shl 8) or
             ((uint32(parseInt(p[2])) and 0xFF) shl 16) or
             ((uint32(parseInt(p[3])) and 0xFF) shl 24)
  except:
     print("Error: Invalid IP\n")
     return @[]

  print("[Download] Connecting to " & host_part & "...\n")
  let fd = lb.socket(2, 1, 0)
  if fd < 0: return @[]

  # SockAddr setup
  var addr_buf: array[16, byte]
  copyMem(addr addr_buf[2], unsafeAddr port, 2)
  copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)

  if lb.connect(fd, addr addr_buf[0], 16) < 0:
    print("Error: Connection Failed.\n")
    return @[]

  # Request
  let req = "GET " & path_str & " HTTP/1.0\r\nHost: " & ip_str & "\r\nConnection: close\r\n\r\n"
  if lb.send(cint(fd), cast[pointer](unsafeAddr req[0]), csize_t(req.len), 0) <= 0:
    print("Error: Send Failed.\n")
    discard lb.close(cint(fd))
    return @[]

  # Mount SFS if needed
  if not sfs.sfs_is_mounted():
    if not sfs.sfs_mount():
      print("Error: Could not mount SFS.\n")
      discard lb.close(cint(fd))
      return @[]

  # Open SFS Stream
  let sfs_h = sfs.sfs_open_write(outfile)
  if sfs_h == nil:
    print("Error: Could not create file " & outfile & "\n")
    discard lb.close(cint(fd))
    return @[]

  print("[Download] Downloading...\n")

  var buf: array[4096, char]
  var header_acc = ""
  var header_parsed = false
  var total_bytes = 0
  var content_len = -1
  var timeout = 0

  while timeout < 10000:
    # Use libc shim which pumps stack
    let n = lb.recv(cint(fd), addr buf[0], 4096, 0)

    if n > 0:
      timeout = 0
      if not header_parsed:
        for i in 0..<n: header_acc.add(buf[i])
        let sep = header_acc.find("\r\n\r\n")
        if sep != -1:
          header_parsed = true
          
          # Try to find Content-Length
          # Quick hacky parse
          let lower_head = header_acc.toLowerAscii()
          let cl_idx = lower_head.find("content-length:")
          if cl_idx != -1:
             let end_line = lower_head.find("\r\n", cl_idx)
             if end_line != -1:
                try:
                  content_len = parseInt(lower_head[cl_idx+15..<end_line].strip())
                except:
                  content_len = -1
          
          let body_start = sep + 4
          if body_start < header_acc.len:
            let chunk = header_acc[body_start..^1]
            sfs.sfs_write_chunk(sfs_h, cast[pointer](unsafeAddr chunk[0]), chunk.len)
            total_bytes += chunk.len
          header_acc = ""
        else:
          if header_acc.len > 8192:
            print("Error: Headers too large.\n")
            break
      else:
        # Stream directly to SFS
        sfs.sfs_write_chunk(sfs_h, addr buf[0], int(n))
        total_bytes += int(n)
        
        # Progress Bar
        if content_len > 0:
           # let pct = (total_bytes * 100) div content_len
           if total_bytes mod 10240 < int(n): print(".")
        else:
           if total_bytes mod 10240 < int(n): print(".")

    elif n == 0:
      break
    else:
      timeout += 1
      # Busy wait / pump handled in recv?
      # Recv calls pump_membrane_stack loop
      # But if we return -1 (EAGAIN), we need to retry.
      # My libc.libc_recv returns 0 on closed? 
      # Actually libc_recv in step 945 waits until data or closed.
      # So n==0 means closed.
      # Wait, libc.nim recv implementation:
      # while true: pump; if data return n; if closed return 0.
      # So it blocks until data.
      # Thus n > 0 always unless closed.
      break

  sfs.sfs_close_write(sfs_h)
  discard lb.close(cint(fd))
  print("\n[Download] Complete. " & $total_bytes & " bytes written to " & outfile & " (Glass Vault).\n")
  return @[]

# Phase 37: HTTP Verification Tool
proc cmd_http_test*(args: seq[string], input: PipelineData): PipelineData =
  if args.len < 1:
    print("Usage: http <host>\n") 
    return @[]
  
  let host = args[0]
  print("Dialing " & host & ":80...\n")
  
  let fd = lnet.net_dial_tcp(host, 80)
  if fd < 0:
    print("Connection Failed! Error: " & $fd & "\n")
    return @[]
    
  print("Connected! Sending GET request...\n")
  discard lnet.net_send(fd, "GET / HTTP/1.0\r\nHost: " & host & "\r\nConnection: close\r\n\r\n")

  print("Waiting for response...\n")
  
  # Simple read loop
  var total = 0
  while true:
    # lb.pump_membrane_stack() 
    let resp = lnet.net_recv(fd, 512)
    if resp.len > 0:
      print(resp)
      total += resp.len
    else:
      # End of stream or empty poll
      break
      
  print("\n[HTTP] Closed. Total bytes: " & $total & "\n")
  lnet.net_close(fd)
  return @[]

proc cmd_http_serve*(args: seq[string], input: PipelineData): PipelineData =
  print("[Server] Starting Nexus Web/1.0...\n")
  
  let port: uint16 = if args.len > 0: uint16(parseInt(args[0])) else: 80
  
  let s = lb.socket(2, 1, 0)
  if s < 0:
    print("Error: Socket creation failed.\n")
    return @[]
    
  # Bind 0.0.0.0:port
  var addr_buf: array[16, byte]
  addr_buf[0] = 2 # AF_INET
  copyMem(addr addr_buf[2], unsafeAddr port, 2)
  # IP 0.0.0.0 is default 0s
  
  if lb.bind_socket(s, addr addr_buf[0], 16) < 0:
    print("Error: Bind failed.\n")
    return @[]
    
  if lb.listen(s, 1) < 0:
    print("Error: Listen failed.\n")
    return @[]
    
  print("[Server] Listening on port " & $port & "...\n")
  
  while true:
    # Accept blocks and pumps stack
    let client = lb.accept(s, nil, nil)
    if client < 0:
      print("Error: Accept failed.\n")
      continue
      
    print("[Server] Client Connected (FD " & $client & ")\n")
    
    var buf: array[1024, char]
    let n = lb.recv(client, addr buf[0], 1024, 0)
    
    if n > 0:
       var req = ""
       for i in 0..<n: req.add(buf[i])
       print("[Server] Request:\n" & req & "\n")
       
       let resp = "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello from Nexus Unikernel!\n"
       discard lb.send(client, unsafeAddr resp[0], uint64(resp.len), 0)
    
    discard lb.close(client)
    print("[Server] Client Closed.\n")
    
    # Just handle one for testing? No, loop forever.
    # But how to exit? Ctrl-C not implemented in NipBox yet?
    # We'll just run forever for MVP.
    
  return @[]

proc cmd_from_json*(args: seq[string], input: PipelineData): PipelineData =
  if input.len == 0: return @[]
  result = @[]

  for inNode in input:
    var body = ""
    for p in inNode.props:
      if p.key == "body":
        body = p.val.s
        break

    if body == "": continue

    try:
      # Find start of JSON if header is present
      let start = body.find('{')
      if start == -1: continue
      let json_str = body[start..^1]

      let j = parseJson(json_str)
      if j.kind == JObject:
        let outNode = newNode("data")
        for k, v in j.fields:
          case v.kind:
            of JString: outNode.addProp(k, newVal(v.getStr()))
            of JInt: outNode.addProp(k, newVal(int(v.getBiggestInt())))
            of JFloat: outNode.addProp(k, newVal($v.getFloat())) # KDL value doesn't support float yet?
            of JBool: outNode.addProp(k, newVal(if v.getBool(): 1 else: 0))
            else: discard
        result.add(outNode)
      elif j.kind == JArray:
        for item in j:
          if item.kind == JObject:
            let outNode = newNode("data")
            for k, v in item.fields:
              case v.kind:
                of JString: outNode.addProp(k, newVal(v.getStr()))
                of JInt: outNode.addProp(k, newVal(int(v.getBiggestInt())))
                else: discard
            result.add(outNode)
    except:
      print("Error: JSON Parse failed.\n")

proc cmd_set*(args: seq[string], input: PipelineData): PipelineData =
  # Syntax: set key = value
  if args.len < 3 or args[1] != "=":
    print("Usage: set <var> = <value>\n")
    last_exit_code = 1
    return @[]
  let key = args[0]
  let value = args[2..^1].join(" ")
  env_table[key] = value
  last_exit_code = 0
  return @[]


proc cmd_help*(args: seq[string], input: PipelineData): PipelineData =
  print("NipBox " & NIPBOX_VERSION & " (Phase 34: Orbital Drop)\n")
  print("Commands: ls, cat, echo, where, http, http.get, http.download, from_json, mount, matrix, set, if, while, help, exit\n")
  return @[]

# --- DISPATCHER ---

proc dispatch_command(name: string, args: seq[string],
    input: PipelineData): PipelineData =
  let cmd = name.toLowerAscii().strip()
  if cmd.len == 0: return input

  case cmd:
    of "ls": return cmd_ls(args, input)

    of "cat": return cmd_cat(args, input)
    of "cp": return cmd_cp(args, input)
    of "mv": return cmd_mv(args, input)
    of "touch": return cmd_touch(args, input)
    of "write": return cmd_write(args, input)
    of "read": return cmd_read(args, input)
    of "edit": return cmd_edit(args, input)
    of "echo": return cmd_echo(args, input)
    of "where": return cmd_where(args, input)
    of "http":
      # Phase 38: The Payload (Direct Socket Usage)
      if args.len < 1:
        print("Usage: http <ip>\n")
        return @[]
      
      let host = args[0]
      print("[NipBox] Dialing " & host & ":80...\n")
      
      # Use libc.socket/connect (Phase 38 Shim)
      let fd = lb.socket(2, 1, 0)
      if fd < 0:
        print("Socket Error\n")
        return @[]
        
      # Parse IP (Quick hack for 10.0.2.2)
      # We need proper parsing but let's assume raw IP for MVP
      var ip_val: uint32 = 0
      try:
        let p = host.split('.')
        ip_val = (uint32(parseInt(p[0])) and 0xFF) or
                 ((uint32(parseInt(p[1])) and 0xFF) shl 8) or
                 ((uint32(parseInt(p[2])) and 0xFF) shl 16) or
                 ((uint32(parseInt(p[3])) and 0xFF) shl 24)
      except:
        print("Error: Invalid IP format (use A.B.C.D)\n")
        return @[]
        
      # Construct SockAddrIn (Layout must match libc.connect hack)
      var addr_buf: array[16, byte]
      # Port 80 (0x0050) -> Big Endian 0x0050? No, htons(80) = 0x5000 on LE?
      # 80 = 0x0050. LE in mem: 50 00.
      # LwIP wants host byte order or network? 
      # connect() shim expects us to pass port as uint16.
      # But the shim casts addr_ptr+2 to uint16*.
      # If we write 80 there, it reads 80.
      
      let port: uint16 = 80
      copyMem(addr addr_buf[2], unsafeAddr port, 2)
      copyMem(addr addr_buf[4], unsafeAddr ip_val, 4)
      
      if lb.connect(fd, addr addr_buf[0], 16) < 0:
        print("Connect Failed\n")
        return @[]
        
      print("[NipBox] Connected! Sending Payload...\n")
      let req = "GET / HTTP/1.0\r\n\r\n"
      discard lb.send(fd, unsafeAddr req[0], uint64(req.len), 0)
      
      print("[NipBox] Waiting for Data...\n")
      var buf: array[1024, char]
      while true:
        let n = lb.recv(fd, addr buf[0], 1024, 0)
        if n > 0:
          var s = ""
          for i in 0..<n: s.add(buf[i])
          print(s)
        else:
          break
          
      print("\n[NipBox] Done.\n")
      return @[]
    of "http.test": return cmd_http_test(args, input)
    of "http.serve": return cmd_http_serve(args, input)
    of "http.get":
      # Phase 30: Spawn in worker with INET pledge only (no file access)
      return spawn_command(cmd_http_get, args, input, PLEDGE_INET or PLEDGE_STDIO)
    of "http.download":
      # Phase 34: Spawn in worker with INET and R/W PATH pledge (needs to write file)
      # PLEDGE_WPATH (0x4) + PLEDGE_INET (0x8) + PLEDGE_STDIO (0x1) = 0xD
      return spawn_command(cmd_http_download, args, input, PLEDGE_INET or
          PLEDGE_WPATH or PLEDGE_STDIO)
    of "from_json": return cmd_from_json(args, input)
    of "mount": return cmd_mount(args, input)
    of "matrix": return cmd_matrix(args, input)
    of "crash": return cmd_crash(args, input)
    of "sys.upgrade": return cmd_upgrade(args, input)
    of "set": return cmd_set(args, input)
    of "help": return cmd_help(args, input)
    of "exit":
      lb.exit(0)
      return @[]
    else:
      print("Error: Command '" & cmd & "' not recognized.\n")
      last_exit_code = 127
      return @[]

# Forward declaration for recursive calls
proc process_pipeline*(line: string)
proc execute_block(lines: seq[string])

proc parse_block(text: string, startIdx: int): tuple[content: string, endIdx: int] =
  # Find matching closing brace
  var depth = 0
  var i = startIdx
  var blockContent = ""
  var started = false

  while i < text.len:
    if text[i] == '{':
      if started:
        blockContent.add('{')
        depth += 1
      else:
        started = true
      i += 1
    elif text[i] == '}':
      if depth > 0:
        blockContent.add('}')
        depth -= 1
        i += 1
      else:
        return (blockContent, i)
    else:
      if started:
        blockContent.add(text[i])
      i += 1

  return (blockContent, i)

proc eval_condition(condLine: string): bool =
  # Execute the condition as a pipeline and check exit code
  last_exit_code = 0
  process_pipeline(condLine)
  return last_exit_code == 0

proc execute_block(lines: seq[string]) =
  for line in lines:
    process_pipeline(line)

proc cmd_if*(fullLine: string) =
  # Parse: if <condition> { <block> }
  let parts = fullLine.strip().splitWhitespace(maxsplit = 1)
  if parts.len < 2:
    print("Usage: if <condition> { ... }\n")
    last_exit_code = 1
    return

  let restLine = parts[1]
  let bracePos = restLine.find('{')
  if bracePos == -1:
    print("Error: if block missing '{'\n")
    last_exit_code = 1
    return

  let condition = restLine[0..<bracePos].strip()
  let (blockContent, _) = parse_block(restLine, bracePos)

  if eval_condition(condition):
    let blockLines = blockContent.splitLines().filterIt(it.strip().len > 0)
    execute_block(blockLines)

  last_exit_code = 0

proc cmd_while*(fullLine: string) =
  # Parse: while <condition> { <block> }
  let parts = fullLine.strip().splitWhitespace(maxsplit = 1)
  if parts.len < 2:
    print("Usage: while <condition> { ... }\n")
    last_exit_code = 1
    return

  let restLine = parts[1]
  let bracePos = restLine.find('{')
  if bracePos == -1:
    print("Error: while block missing '{'\n")
    last_exit_code = 1
    return

  let condition = restLine[0..<bracePos].strip()
  let (blockContent, _) = parse_block(restLine, bracePos)
  let blockLines = blockContent.splitLines().filterIt(it.strip().len > 0)

  while eval_condition(condition):
    execute_block(blockLines)

  last_exit_code = 0

proc process_pipeline*(line: string) =
  let expandedLine = expand_vars(line)
  let cleanLine = expandedLine.strip()
  if cleanLine.len == 0 or cleanLine.startsWith("#"): return

  # Check for control flow
  if cleanLine.startsWith("if "):
    cmd_if(cleanLine)
    return
  elif cleanLine.startsWith("while "):
    cmd_while(cleanLine)
    return

  var redirectionFile = ""
  var pipelineText = cleanLine

  # Find redirection at the end of the line
  let lastGt = cleanLine.rfind('>')
  if lastGt != -1:
    # Check if this > is likely a redirection (preceded by space or end of command)
    # Simple heuristic: if it's the last segment and followed by a "path-like" string
    let potentialFile = cleanLine[lastGt+1..^1].strip()
    if potentialFile.len > 0 and not potentialFile.contains(' '):
      # Most likely a redirection
      pipelineText = cleanLine[0..<lastGt].strip()
      redirectionFile = potentialFile

  let segments = pipelineText.split("|")
  var current_blood: PipelineData = @[]
  last_exit_code = 0

  for segIdx, seg in segments:
    let parts = seg.strip().splitWhitespace()
    if parts.len == 0: continue

    let cmdName = parts[0]
    let args = if parts.len > 1: parts[1..^1] else: @[]

    current_blood = dispatch_command(cmdName, args, current_blood)

    # Exit code: success if we got data, failure if empty (unless piped)
    if current_blood.len == 0:
      if segIdx < segments.len - 1:
        break
      else:
        last_exit_code = 1

  if current_blood.len > 0:
    if redirectionFile.len > 0:
      # Write to file (Sovereign Write)
      var content = ""
      for node in current_blood:
        content.add(node.render())

      let fd = lb.open(redirectionFile.cstring, 577) # O_WRONLY | O_CREAT | O_TRUNC
      if fd >= 0:
        discard lb.write(fd, cast[pointer](unsafeAddr content[0]), csize_t(content.len))
        discard lb.close(fd)
        print("[VFS] Data diverted to: " & redirectionFile & "\n")
      else:
        print("[VFS] Error: Could not open '" & redirectionFile & "' for diversion.\n")
    else:
      render_output(current_blood)

# --- BOOTSTRAP ---

proc run_script(path: string) =
  let fd = lb.open(path.cstring, 0)
  if fd < 0: return

  var buf = newString(8192)
  let n = lb.read(fd, addr buf[0], 8192)
  if n > 0: buf.setLen(n)
  discard lb.close(fd)

  if n > 0:
    var currentLine = ""
    for c in buf:
      if c == '\n' or c == '\r':
        if currentLine.strip().len > 0:
          process_pipeline(currentLine)
        currentLine = ""
      else:
        currentLine.add(c)
    if currentLine.strip().len > 0:
      process_pipeline(currentLine)

# --- MAIN ---

proc nipbox_main*() =
  # DIAGNOSTIC: Very first thing - prove we're executing
  print("[NIPBOX] Entry point reached!\n")
  
  # Phase 30: Pledge Safety
  # NipBox is the Shell, so it needs broad permissions, but we can restrict RPATH/WPATH to specific zones
  # For now, we PLEDGE_ALL because the shell needs to explore
  # In future (SPEC-401), we drop PLEDGE_INET unless authorized
  discard lb.pledge(PLEDGE_ALL)

  # Initialize the Biosuit
  print("[NipBox] Booting...\n")
    # lb.membrane_init() - Handled by Kernel
  # term.term_init() # Phase 26: Visual Cortex Init - DISABLED

  print("\n\x1b[1;32m╔═══════════════════════════════════════╗\x1b[0m\n")
  print("\x1b[1;32m║   SOVEREIGN SUPERVISOR v0.8.7         ║\x1b[0m\n")
  print("\x1b[1;32m║   PHASE 21: THE TELEPORTER ACTIVATED  ║\x1b[0m\n")
  print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")

  # Phase 38: Boot Script
  run_script("/init.nsh")
  print("\x1b[1;32m╚═══════════════════════════════════════╝\x1b[0m\n\n")
 

  print("\x1b[1;33mroot@nexus:# \x1b[0m")
  var inputBuffer: string = ""
  var loop_counter: uint64 = 0

  while true:
    # Phase 21: Teleporter Logic (Network Pipeline)
    # Check network sockets and pipe data
    # (Simplified for now - just echo)
    lb.pump_membrane_stack()

    var c: char
    let n = lb.read(0, addr c, 1)
    if n > 0:
      if c == '\n' or c == '\r':
        print("\n")
        process_pipeline(inputBuffer)
        inputBuffer = ""
        print("\x1b[1;33mroot@nexus:# \x1b[0m")
      elif c == '\b' or c == char(127):
        if inputBuffer.len > 0:
          print("\b \b")
          inputBuffer.setLen(inputBuffer.len - 1)
      else:
        inputBuffer.add(c)
        var s = ""
        s.add(c)
        print(s)
    else:
      # Cooperative multitasking support
      lb.yield_fiber()

when isMainModule: nipbox_main()
