## nimpak/nippels_cli.nim
## Enhanced CLI commands for Nippels management
##
## Provides comprehensive command-line interface for:
## - Creating Nippels with profiles and customization
## - Managing profiles and isolation levels
## - Verifying integrity with merkle trees
## - Querying via UTCP protocol
## - Listing and inspecting Nippels

import std/[strutils, strformat, tables, terminal, options]
import utils/resultutils as nipresult
import nippels, nippel_types, profile_manager, merkle_tree, utcp_protocol

# =============================================================================
# CLI Output Formatting
# =============================================================================

proc printSuccess*(msg: string) =
  stdout.styledWriteLine(fgGreen, "✓ ", resetStyle, msg)

proc printError*(msg: string) =
  stderr.styledWriteLine(fgRed, "✗ ", resetStyle, msg)

proc printWarning*(msg: string) =
  stdout.styledWriteLine(fgYellow, "⚠ ", resetStyle, msg)

proc printInfo*(msg: string) =
  stdout.styledWriteLine(fgCyan, "ℹ ", resetStyle, msg)

proc printHeader*(msg: string) =
  stdout.styledWriteLine(styleBright, fgWhite, msg, resetStyle)

proc printKeyValue*(key: string, value: string, indent: int = 0) =
  let spaces = " ".repeat(indent)
  stdout.styledWrite(spaces, fgCyan, key, resetStyle, ": ")
  stdout.styledWriteLine(fgWhite, value, resetStyle)

# =============================================================================
# Command: nip cell create
# =============================================================================

type
  CreateOptions* = object
    name*: string
    profile*: SecurityProfile
    isolation*: IsolationLevel
    overrides*: ProfileOverrides
    description*: string

proc cmdCellCreate*(manager: var NippelManager, opts: CreateOptions): int =
  ## Create a new Nippel with specified profile and customization
  ## Returns: 0 on success, non-zero on error

  printHeader(fmt"Creating Nippel: {opts.name}")
  printInfo(fmt"Profile: {opts.profile}")
  printInfo(fmt"Isolation: {opts.isolation}")

  # Apply profile overrides if specified
  var finalOverrides = opts.overrides
  if opts.description.len > 0:
    printInfo(fmt"Description: {opts.description}")

  # Create the Nippel
  let createResult = manager.createNippel(
    opts.name,
    opts.profile,
    finalOverrides
  )

  if createResult.isOk:
    let nippel = createResult.value
    printSuccess(fmt"Nippel '{opts.name}' created successfully")
    printKeyValue("Path", nippel.cellRoot, 2)
    printKeyValue("Profile", $nippel.profile, 2)
    printKeyValue("Isolation", $nippel.isolationLevel, 2)
    printKeyValue("UTCP Address", formatUTCPAddress(nippel.utcpAddress), 2)
    if nippel.merkleRoot.len > 0:
      printKeyValue("Merkle Root", nippel.merkleRoot[0..15] & "...", 2)
    return 0
  else:
    printError(fmt"Failed to create Nippel: {createResult.error}")
    return 1

# =============================================================================
# Command: nip cell list
# =============================================================================

proc cmdCellList*(manager: NippelManager, verbose: bool = false): int =
  ## List all Nippels
  ## Returns: 0 on success

  let nippels = manager.listNippels()

  if nippels.len == 0:
    printInfo("No Nippels found")
    return 0

  printHeader(fmt"Nippels ({nippels.len} total)")
  echo ""

  for nippel in nippels:
    let activeMarker = if manager.isNippelActive(nippel.name): " [ACTIVE]" else: ""
    stdout.styledWrite(styleBright, fgWhite, nippel.name, resetStyle)
    if activeMarker.len > 0:
      stdout.styledWrite(fgGreen, activeMarker, resetStyle)
    echo ""

    printKeyValue("Profile", $nippel.profile, 2)
    printKeyValue("Isolation", $nippel.isolation, 2)
    printKeyValue("Created", $nippel.created, 2)

    if verbose:
      printKeyValue("Size", $(nippel.size div 1024) & " KB", 2)
      printKeyValue("Packages", $nippel.packageCount, 2)

    echo ""

  return 0

# =============================================================================
# Command: nip cell activate
# =============================================================================

proc cmdCellActivate*(manager: var NippelManager, name: string): int =
  ## Activate a Nippel
  ## Returns: 0 on success, non-zero on error

  printInfo(fmt"Activating Nippel: {name}")

  let activateResult = manager.activateNippel(name)

  if activateResult.isOk:
    printSuccess(fmt"Nippel '{name}' activated successfully")
    printInfo("Environment variables updated")
    printInfo("Namespaces entered (if applicable)")
    return 0
  else:
    printError(fmt"Failed to activate Nippel: {activateResult.error}")
    return 1

# =============================================================================
# Command: nip cell deactivate
# =============================================================================

proc cmdCellDeactivate*(manager: var NippelManager, name: string): int =
  ## Deactivate a Nippel
  ## Returns: 0 on success, non-zero on error

  printInfo(fmt"Deactivating Nippel: {name}")

  let deactivateResult = manager.deactivateNippel(name)

  if deactivateResult.isOk:
    printSuccess(fmt"Nippel '{name}' deactivated successfully")
    printInfo("Namespaces cleaned up")
    return 0
  else:
    printError(fmt"Failed to deactivate Nippel: {deactivateResult.error}")
    return 1

# =============================================================================
# Command: nip cell profile list
# =============================================================================

proc cmdCellProfileList*(): int =
  ## List all available security profiles
  ## Returns: 0 on success

  printHeader("Available Security Profiles")
  echo ""

  let profiles = [
    (SecurityProfile.Homestation, "Default profile for home desktop systems"),
    (SecurityProfile.Workstation, "Mobile/portable systems with full isolation"),
    (SecurityProfile.Server, "Server systems with strict security"),
    (SecurityProfile.Satellite, "Portable environments for USB/external drives"),
    (SecurityProfile.NetworkIOT, "IoT devices with minimal resources")
  ]

  for (profile, desc) in profiles:
    stdout.styledWriteLine(styleBright, fgWhite, $profile, resetStyle)
    printKeyValue("Description", desc, 2)

    # Load profile settings to show details
    var pm = newProfileManager()
    let settings = pm.loadProfile(profile)
    printKeyValue("Isolation", $settings.isolationLevel, 2)
    printKeyValue("Desktop Integration", $settings.desktopIntegration, 2)
    printKeyValue("Network Access", $settings.networkAccess, 2)
    printKeyValue("Auditing", $settings.auditingEnabled, 2)
    echo ""

  return 0

# =============================================================================
# Command: nip cell profile show
# =============================================================================

proc cmdCellProfileShow*(manager: NippelManager, name: string): int =
  ## Show profile settings for a specific Nippel
  ## Returns: 0 on success, non-zero on error

  let nippelOpt = manager.getNippel(name)
  if nippelOpt.isNone:
    printError(fmt"Nippel '{name}' not found")
    return 1

  let nippel = nippelOpt.get

  printHeader(fmt"Profile for Nippel: {name}")
  echo ""

  printKeyValue("Profile", $nippel.profile)
  printKeyValue("Isolation", $nippel.isolationLevel)
  printKeyValue("Desktop Integration", $nippel.profileSettings.desktopIntegration)
  printKeyValue("Network Access", $nippel.profileSettings.networkAccess)
  printKeyValue("Auditing", $nippel.profileSettings.auditingEnabled)

  if nippel.profileSettings.resourceLimits.maxMemory > 0:
    printKeyValue("Max Memory", fmt"{nippel.profileSettings.resourceLimits.maxMemory} bytes")
  if nippel.profileSettings.resourceLimits.maxCPU > 0:
    printKeyValue("Max CPU", fmt"{nippel.profileSettings.resourceLimits.maxCPU}%")

  return 0

# =============================================================================
# Command: nip cell profile set
# =============================================================================

proc cmdCellProfileSet*(manager: var NippelManager, name: string, profile: SecurityProfile): int =
  ## Change the profile of an existing Nippel
  ## Returns: 0 on success, non-zero on error

  printInfo(fmt"Changing profile for Nippel '{name}' to {profile}")

  let changeResult = manager.changeNippelProfile(name, profile)

  if changeResult.isOk:
    printSuccess(fmt"Profile changed successfully")
    printInfo("Nippel must be reactivated for changes to take effect")
    return 0
  else:
    printError(fmt"Failed to change profile: {changeResult.error}")
    return 1

# =============================================================================
# Command: nip cell verify
# =============================================================================

proc cmdCellVerify*(manager: NippelManager, name: string): int =
  ## Verify integrity of a Nippel using merkle tree
  ## Returns: 0 if verified, non-zero on error or verification failure

  printInfo(fmt"Verifying Nippel: {name}")

  let nippelOpt = manager.getNippel(name)
  if nippelOpt.isNone:
    printError(fmt"Nippel '{name}' not found")
    return 1

  let nippel = nippelOpt.get

  # Get merkle tree for this Nippel
  if not manager.merkleTrees.hasKey(name):
    printWarning("No merkle tree found for this Nippel")
    return 2

  let tree = manager.merkleTrees[name]

  # Verify the tree
  let verifyResult = verifyTree(tree)

  if verifyResult.isOk and verifyResult.value:
    printSuccess("Merkle tree verification passed")
    printKeyValue("Root Hash", nippel.merkleRoot[0..15] & "...", 2)
    printKeyValue("Files Verified", $tree.leafCount, 2)
    return 0
  elif verifyResult.isOk:
    printError("Merkle tree verification FAILED")
    printWarning("Nippel integrity may be compromised")
    return 3
  else:
    printError(fmt"Verification error: {verifyResult.error}")
    return 4

# =============================================================================
# Command: nip cell query
# =============================================================================

proc cmdCellQuery*(manager: NippelManager, address: string): int =
  ## Query a Nippel via UTCP address
  ## Returns: 0 on success, non-zero on error

  printInfo(fmt"Querying UTCP address: {address}")

  # Parse UTCP address (validate format)
  let parseResult = parseUTCPAddress(address)
  if parseResult.isErr:
    printError(fmt"Invalid UTCP address: {parseResult.error}")
    return 1

  # Extract Nippel name from resource path
  # The simple UTCPAddress from nippel_types has resource as the full path
  # Format: /nippel/name or /nippel/name/...
  # But parseUTCPAddress returns the complex version from utcp_protocol
  # For now, just extract the name from the address string
  let addressParts = address.split('/')
  if addressParts.len < 4:
    printError("UTCP address must be in format: utcp://host/nippel/name")
    return 1

  let nippelName = addressParts[3]

  # Get Nippel
  let nippelOpt = manager.getNippel(nippelName)
  if nippelOpt.isNone:
    printError(fmt"Nippel '{nippelName}' not found")
    return 1

  let nippel = nippelOpt.get

  # Determine what to query based on resource path
  if addressParts.len == 4:
    # Query Nippel state (no sub-resource specified)
    printHeader(fmt"Nippel State: {nippelName}")
    echo ""
    printKeyValue("Name", nippel.name)
    printKeyValue("Profile", $nippel.profile)
    printKeyValue("Isolation", $nippel.isolationLevel)
    printKeyValue("Active", $manager.isNippelActive(nippelName))
    printKeyValue("Created", $nippel.created)
    printKeyValue("UTCP Address", formatUTCPAddress(nippel.utcpAddress))
  elif addressParts.len >= 5:
    let resource = addressParts[4]
    case resource
    of "state":
      # Same as above
      printHeader(fmt"Nippel State: {nippelName}")
      echo ""
      printKeyValue("Name", nippel.name)
      printKeyValue("Profile", $nippel.profile)
      printKeyValue("Isolation", $nippel.isolationLevel)
      printKeyValue("Active", $manager.isNippelActive(nippelName))
    of "merkle":
      # Query merkle tree
      if manager.merkleTrees.hasKey(nippelName):
        let tree = manager.merkleTrees[nippelName]
        printHeader(fmt"Merkle Tree: {nippelName}")
        echo ""
        printKeyValue("Root Hash", nippel.merkleRoot[0..15] & "...")
        printKeyValue("Leaves", $tree.leafCount)
        printKeyValue("Algorithm", "xxh3")
      else:
        printWarning("No merkle tree found")
    of "profile":
      # Query profile settings
      printHeader(fmt"Profile Settings: {nippelName}")
      echo ""
      printKeyValue("Profile", $nippel.profile)
      printKeyValue("Isolation", $nippel.isolationLevel)
      printKeyValue("Desktop Integration", $nippel.profileSettings.desktopIntegration)
      printKeyValue("Network Access", $nippel.profileSettings.networkAccess)
    else:
      printError(fmt"Unknown resource: {resource}")
      return 1

  return 0

# =============================================================================
# Command: nip cell info
# =============================================================================

proc cmdCellInfo*(manager: NippelManager, name: string): int =
  ## Show detailed information about a Nippel
  ## Returns: 0 on success, non-zero on error

  let nippelOpt = manager.getNippel(name)
  if nippelOpt.isNone:
    printError(fmt"Nippel '{name}' not found")
    return 1

  let nippel = nippelOpt.get
  let isActive = manager.isNippelActive(name)

  printHeader(fmt"Nippel Information: {name}")
  echo ""

  # Basic info
  stdout.styledWriteLine(styleBright, "Basic Information", resetStyle)
  printKeyValue("Name", nippel.name, 2)
  printKeyValue("Active", $isActive, 2)
  printKeyValue("Created", $nippel.created, 2)
  printKeyValue("Root Path", nippel.cellRoot, 2)
  echo ""

  # Profile info
  stdout.styledWriteLine(styleBright, "Profile Settings", resetStyle)
  printKeyValue("Profile", $nippel.profile, 2)
  printKeyValue("Isolation", $nippel.isolationLevel, 2)
  printKeyValue("Desktop Integration", $nippel.profileSettings.desktopIntegration, 2)
  printKeyValue("Network Access", $nippel.profileSettings.networkAccess, 2)
  printKeyValue("Auditing", $nippel.profileSettings.auditingEnabled, 2)
  echo ""

  # UTCP info
  stdout.styledWriteLine(styleBright, "UTCP Addressing", resetStyle)
  printKeyValue("Address", formatUTCPAddress(nippel.utcpAddress), 2)
  printKeyValue("Scheme", nippel.utcpAddress.scheme, 2)
  printKeyValue("Host", nippel.utcpAddress.host, 2)
  printKeyValue("Resource", nippel.utcpAddress.resource, 2)
  echo ""

  # Merkle tree info
  if manager.merkleTrees.hasKey(name):
    let tree = manager.merkleTrees[name]
    stdout.styledWriteLine(styleBright, "Integrity Verification", resetStyle)
    printKeyValue("Merkle Root", nippel.merkleRoot[0..15] & "...", 2)
    printKeyValue("Files Tracked", $tree.leafCount, 2)
    printKeyValue("Algorithm", "xxh3", 2)
    echo ""

  # Resource limits
  if nippel.profileSettings.resourceLimits.maxMemory > 0 or
     nippel.profileSettings.resourceLimits.maxCPU > 0:
    stdout.styledWriteLine(styleBright, "Resource Limits", resetStyle)
    if nippel.profileSettings.resourceLimits.maxMemory > 0:
      printKeyValue("Max Memory", fmt"{nippel.profileSettings.resourceLimits.maxMemory} bytes", 2)
    if nippel.profileSettings.resourceLimits.maxCPU > 0:
      printKeyValue("Max CPU", fmt"{nippel.profileSettings.resourceLimits.maxCPU}%", 2)
    echo ""

  return 0

# =============================================================================
# Command: nip cell delete
# =============================================================================

proc cmdCellDelete*(manager: var NippelManager, name: string, force: bool = false): int =
  ## Delete a Nippel
  ## Returns: 0 on success, non-zero on error

  # Check if Nippel exists
  let nippelOpt = manager.getNippel(name)
  if nippelOpt.isNone:
    printError(fmt"Nippel '{name}' not found")
    return 1

  # Check if active
  if manager.isNippelActive(name) and not force:
    printError(fmt"Nippel '{name}' is currently active")
    printInfo("Deactivate it first or use --force")
    return 1

  # Deactivate if active
  if manager.isNippelActive(name):
    printWarning("Force deleting active Nippel")
    discard manager.deactivateNippel(name)

  printInfo(fmt"Deleting Nippel: {name}")

  let deleteResult = manager.deleteNippel(name)

  if deleteResult.isOk:
    printSuccess(fmt"Nippel '{name}' deleted successfully")
    return 0
  else:
    printError(fmt"Failed to delete Nippel: {deleteResult.error}")
    return 1
