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

## nimpak/nippels.nim
## Nippels: Lightweight, namespace-based application isolation
##
## Nippels provides superior application isolation and environment management with:
## - Zero-overhead isolation using Linux kernel namespaces
## - Intelligent dependency sharing with deduplication via CAS
## - Instant startup with no runtime overhead
## - Perfect system integration with native performance
## - Atomic updates and rollbacks
## - Cryptographic verification and security
## - Profile-based security settings
## - XDG Base Directory enforcement
## - UTCP protocol support for AI-addressability

import std/[os, strutils, times, json, tables, options]
when defined(posix):
  import posix
import utils/resultutils as nipresult
import nippel_types
import profile_manager
import namespace_subsystem as ns
import xdg_enforcer as xdg
import merkle_tree as mt
# Note: UTCP address assignment already implemented using nippel_types.UTCPAddress
# Full utcp_protocol module available for future advanced features

# Re-export types from nippel_types for convenience
export nippel_types

# =============================================================================
# Additional Manager Types
# =============================================================================
# Manager-Specific Types (Core types are in nippel_types.nim)
# =============================================================================

type
  # Global Configuration
  NippelGlobalConfig* = object
    defaultProfile*: SecurityProfile
    defaultIsolation*: IsolationLevel
    maxCells*: int
    maxCellSize*: int64
    allowNetworkByDefault*: bool
    enableDesktopIntegration*: bool

  # Nippel Manager
  NippelManager* = object
    cellsRoot*: string
    activeCells*: Table[string, Nippel]
    globalConfig*: NippelGlobalConfig
    # Task 8.1: Profile Manager integration
    profileManager*: ProfileManager
    # Task 8.4: Merkle Tree tracking
    merkleTrees*: Table[string, mt.MerkleTree]  # Nippel name -> Merkle tree

type
  # Nippel Info (for listing)
  NippelInfo* = object
    name*: string
    profile*: SecurityProfile
    isolation*: IsolationLevel
    created*: DateTime
    lastUsed*: DateTime
    size*: int64
    packageCount*: int

# =============================================================================
# Error Handling (Requirement 1.2)
# =============================================================================

proc newNippelError*(code: ErrorCode, msg: string, nippelName: string = "",
                     context: JsonNode = nil): ref NippelError =
  ## Create a new NippelError with context
  result = newException(NippelError, msg)
  result.code = code
  result.nippelName = nippelName
  result.context = if context.isNil: newJObject() else: context

proc logNippelError*(err: ref NippelError) =
  ## Log a Nippel error with full context
  echo "❌ [", err.code, "] ", err.msg
  if err.nippelName.len > 0:
    echo "   Nippel: ", err.nippelName
  if not err.context.isNil and err.context.len > 0:
    echo "   Context: ", err.context.pretty()

# =============================================================================
# Profile Management (Requirement 6.1-6.8)
# =============================================================================

proc getDefaultProfileSettings*(profile: SecurityProfile): ProfileSettings =
  ## Get default settings for a security profile
  case profile:
  of Workstation:
    ProfileSettings(
      isolationLevel: Standard,
      desktopIntegration: true,
      networkAccess: Full,
      resourceLimits: ResourceLimits(
        maxMemory: 8 * 1024 * 1024 * 1024,  # 8GB
        maxCpu: 0.9,
        maxDisk: 10 * 1024 * 1024 * 1024,   # 10GB
        maxProcesses: 200,
        maxOpenFiles: 2048
      ),
      auditingEnabled: false
    )
  of Homestation:
    ProfileSettings(
      isolationLevel: Standard,
      desktopIntegration: true,
      networkAccess: Relaxed,
      resourceLimits: ResourceLimits(
        maxMemory: 4 * 1024 * 1024 * 1024,  # 4GB
        maxCpu: 0.8,
        maxDisk: 5 * 1024 * 1024 * 1024,    # 5GB
        maxProcesses: 150,
        maxOpenFiles: 1024
      ),
      auditingEnabled: false
    )
  of Satellite:
    ProfileSettings(
      isolationLevel: Strict,
      desktopIntegration: true,
      networkAccess: Limited,
      resourceLimits: ResourceLimits(
        maxMemory: 2 * 1024 * 1024 * 1024,  # 2GB
        maxCpu: 0.7,
        maxDisk: 3 * 1024 * 1024 * 1024,    # 3GB
        maxProcesses: 100,
        maxOpenFiles: 512
      ),
      auditingEnabled: true
    )
  of NetworkIOT:
    ProfileSettings(
      isolationLevel: Strict,
      desktopIntegration: false,
      networkAccess: Limited,
      resourceLimits: ResourceLimits(
        maxMemory: 512 * 1024 * 1024,       # 512MB
        maxCpu: 0.5,
        maxDisk: 1 * 1024 * 1024 * 1024,    # 1GB
        maxProcesses: 50,
        maxOpenFiles: 256
      ),
      auditingEnabled: true
    )
  of Server:
    ProfileSettings(
      isolationLevel: Strict,
      desktopIntegration: false,
      networkAccess: Full,
      resourceLimits: ResourceLimits(
        maxMemory: 16 * 1024 * 1024 * 1024, # 16GB
        maxCpu: 1.0,
        maxDisk: 50 * 1024 * 1024 * 1024,   # 50GB
        maxProcesses: 500,
        maxOpenFiles: 4096
      ),
      auditingEnabled: true
    )

proc loadProfile*(profile: SecurityProfile): ProfileSettings =
  ## Load profile settings (Requirement 6.7)
  result = getDefaultProfileSettings(profile)

# =============================================================================
# UTCP Address Support (Requirement 12.1-12.5)
# =============================================================================

proc assignUTCPAddress*(nippel: var Nippel, hostname: string = "localhost"): UTCPAddress =
  ## Assign a UTCP address to a Nippel (Requirement 12.1)
  result = UTCPAddress(
    scheme: "utcp://",
    host: hostname,
    resource: "nippel/" & nippel.name,
    port: 0  # Default port, can be customized
  )
  nippel.utcpAddress = result

# Note: formatUTCPAddress is defined in nippel_types.nim

# =============================================================================
# Nippel Manager (Requirement 1.1)
# =============================================================================

proc newNippelManager*(cellsRoot: string = ""): NippelManager =
  ## Create a new NippelManager
  let root = if cellsRoot.len > 0: cellsRoot
            else: getHomeDir() / ".nip" / "cells"

  NippelManager(
    cellsRoot: root,
    activeCells: initTable[string, Nippel](),
    globalConfig: NippelGlobalConfig(
      defaultProfile: Homestation,
      defaultIsolation: Standard,
      maxCells: 50,
      maxCellSize: 10 * 1024 * 1024 * 1024,  # 10GB
      allowNetworkByDefault: true,
      enableDesktopIntegration: true
    ),
    # Task 8.1: Initialize Profile Manager
    profileManager: newProfileManager(),
    # Task 8.4: Initialize Merkle Trees table
    merkleTrees: initTable[string, mt.MerkleTree]()
  )

proc createNippel*(manager: var NippelManager, name: string,
                   profile: SecurityProfile = Homestation,
                   overrides: ProfileOverrides = ProfileOverrides()): nipresult.Result[Nippel, string] =
  ## Create a new Nippel with profile support (Requirement 6.1-6.8)
  ## Task 8.1: Integrated with ProfileManager
  try:
    # Check if cell already exists
    let cellRoot = manager.cellsRoot / name
    if dirExists(cellRoot):
      let error = newNippelError(ErrNippelAlreadyExists,
        "Nippel already exists: " & name, name)
      logNippelError(error)
      return err[Nippel]("Nippel already exists: " & name)

    # Generate unique ID
    let cellId = name.toLowerAscii().replace(" ", "-") & "-" & $epochTime().int

    # Task 8.1: Load profile settings using ProfileManager
    let profileSettings = manager.profileManager.loadProfile(profile)

    # Create cell directory structure
    createDir(cellRoot)
    createDir(cellRoot / "Programs")
    createDir(cellRoot / "System" / "Index")

    # Task 8.3: Create XDG directories using XDG Enforcer
    echo "🔧 Creating XDG directory structure..."
    let xdgResult = xdg.createXDGStructure(cellRoot, profile)
    if xdgResult.isErr:
      return err[Nippel]("Failed to create XDG structure: " & xdgResult.error)

    let xdgDirs = xdgResult.value

    # Task 12.1: Lazy namespace creation - defer until first activation
    # Namespaces are no longer created during Nippel creation
    # They will be created on-demand during activateNippel()
    var namespaceHandle = none(NamespaceHandle)
    echo "ℹ️  Namespace creation deferred (lazy initialization)"

    # Create Nippel object
    var nippel = Nippel(
      name: name,
      id: cellId,
      created: now(),
      lastUsed: now(),
      profile: profile,
      profileSettings: profileSettings,
      isolationLevel: profileSettings.isolationLevel,
      cellRoot: cellRoot,
      xdgDirs: xdgDirs,
      namespaceHandle: namespaceHandle,
      casEntries: @[],
      merkleRoot: "",
      utcpAddress: UTCPAddress(),  # Will be assigned later
      packages: @[],
      metadata: newJObject(),
      version: "1.0"
    )

    # Task 8.5: Assign UTCP address
    discard assignUTCPAddress(nippel)

    # Task 8.1: Apply profile overrides if provided
    if overrides.isolationLevel.isSome or overrides.desktopIntegration.isSome or
       overrides.networkAccess.isSome or overrides.resourceLimits.isSome or
       overrides.auditingEnabled.isSome:
      echo "🔧 Applying profile customizations..."
      let customizeResult = customizeProfile(nippel, overrides)
      if customizeResult.isErr:
        return err[Nippel]("Failed to apply profile overrides: " & customizeResult.error)

    # Task 8.4: Build initial merkle tree
    echo "🔧 Building initial merkle tree..."
    let treeResult = mt.buildTreeFromFiles(@[], "xxh3")  # Empty tree initially
    if treeResult.isErr:
      return err[Nippel]("Failed to build merkle tree: " & $treeResult.error.msg)

    let tree = treeResult.get()
    nippel.merkleRoot = mt.getRootHash(tree)

    # Store merkle tree in manager
    manager.merkleTrees[name] = tree

    echo "✅ Merkle tree created with root: ", nippel.merkleRoot

    # Save cell configuration
    let cellConfig = %*{
      "nippel": {
        "name": nippel.name,
        "id": nippel.id,
        "version": nippel.version,
        "created": $nippel.created,
        "lastUsed": $nippel.lastUsed
      },
      "profile": {
        "type": $nippel.profile,
        "isolation": $nippel.isolationLevel,
        "desktopIntegration": nippel.profileSettings.desktopIntegration,
        "networkAccess": $nippel.profileSettings.networkAccess,
        "auditingEnabled": nippel.profileSettings.auditingEnabled
      },
      "paths": {
        "root": nippel.cellRoot,
        "data": nippel.xdgDirs.dataHome,
        "config": nippel.xdgDirs.configHome,
        "cache": nippel.xdgDirs.cacheHome,
        "state": nippel.xdgDirs.stateHome,
        "runtime": nippel.xdgDirs.runtimeDir
      },
      "storage": {
        "merkle_root": nippel.merkleRoot,
        "cas_entries": nippel.casEntries.len,
        "total_size": 0
      },
      "network": {
        "utcp_address": formatUTCPAddress(nippel.utcpAddress)
      },
      "packages": newJArray()
    }

    writeFile(cellRoot / "cell.json", cellConfig.pretty())

    echo "🎉 Created Nippel: ", name
    echo "   Profile: ", profile
    echo "   Isolation: ", nippel.isolationLevel
    echo "   Path: ", cellRoot
    echo "   UTCP: ", formatUTCPAddress(nippel.utcpAddress)

    return ok[Nippel](nippel)

  except Exception as e:
    let error = newNippelError(ErrNippelAlreadyExists,
      "Failed to create Nippel: " & e.msg, name)
    logNippelError(error)
    return err[Nippel]("Failed to create Nippel: " & e.msg)

# =============================================================================
# Nippel Activation/Deactivation (Task 8.2)
# =============================================================================

proc activateNippel*(manager: var NippelManager, nippelName: string): nipresult.Result[bool, string] =
  ## Activate a Nippel by entering its namespaces (Task 8.2)
  try:
    # Check if already active
    if nippelName in manager.activeCells:
      return err[bool]("Nippel already active: " & nippelName)

    # Load the Nippel
    let cellRoot = manager.cellsRoot / nippelName
    if not dirExists(cellRoot):
      return err[bool]("Nippel not found: " & nippelName)

    let configPath = cellRoot / "cell.json"
    if not fileExists(configPath):
      return err[bool]("Nippel configuration not found: " & nippelName)

    # Parse configuration
    let config = parseJson(readFile(configPath))

    # Create Nippel object
    var nippel = Nippel(
      name: config["nippel"]["name"].getStr(),
      id: config["nippel"]["id"].getStr(),
      created: parse(config["nippel"]["created"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
      lastUsed: now(),
      profile: parseEnum[SecurityProfile](config["profile"]["type"].getStr()),
      profileSettings: manager.profileManager.loadProfile(
        parseEnum[SecurityProfile](config["profile"]["type"].getStr())
      ),
      isolationLevel: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
      cellRoot: cellRoot,
      xdgDirs: XDGDirectories(
        dataHome: config["paths"]["data"].getStr(),
        configHome: config["paths"]["config"].getStr(),
        cacheHome: config["paths"]["cache"].getStr(),
        stateHome: config["paths"]["state"].getStr(),
        runtimeDir: config["paths"]["runtime"].getStr()
      ),
      namespaceHandle: none(NamespaceHandle),
      casEntries: @[],
      merkleRoot: config["storage"]["merkle_root"].getStr(),
      utcpAddress: UTCPAddress(),
      packages: @[],
      metadata: newJObject(),
      version: config["nippel"]["version"].getStr()
    )

    # Task 12.1: Lazy namespace creation - create on first activation
    if nippel.isolationLevel != None:
      echo "🔄 Activating Nippel with ", nippel.isolationLevel, " isolation..."

      # Get namespace configuration
      let nsConfig = ns.getNamespaceHandle(nippel.isolationLevel)

      # Create namespaces (lazy initialization)
      echo "🔧 Creating namespaces (lazy initialization)..."
      let createResult = ns.createNamespaces(nsConfig)
      if createResult.isErr:
        return err[bool]("Failed to create namespaces: " & createResult.error)

      let nsHandle = createResult.value

      # Only store namespace handle if namespaces were actually created
      if nsHandle.mountNS or nsHandle.pidNS or nsHandle.networkNS or
         nsHandle.ipcNS or nsHandle.userNS or nsHandle.utsNS:
        nippel.namespaceHandle = some(nsHandle)

        # Enter the namespaces
        let enterResult = ns.enterNamespace(nsHandle)
        if enterResult.isErr:
          return err[bool]("Failed to enter namespaces: " & enterResult.error)

        echo "✅ Namespaces created and entered successfully"
      else:
        echo "ℹ️  Namespace creation skipped (insufficient privileges)"
    else:
      echo "ℹ️  Activating without namespace isolation"

    # Task 8.3: Set XDG environment variables
    echo "🔧 Setting XDG environment..."
    let xdgEnvResult = xdg.setXDGEnvironment(nippel.xdgDirs)
    if xdgEnvResult.isErr:
      return err[bool]("Failed to set XDG environment: " & xdgEnvResult.error)

    # Task 8.3: Redirect legacy paths (if portable mode)
    if xdg.getXDGStrategy(nippel.profile) == xdg.Portable:
      echo "🔧 Redirecting legacy paths..."
      let redirectResult = xdg.redirectLegacyPaths(nippel.xdgDirs, nippel.cellRoot)
      if redirectResult.isErr:
        echo "⚠️  Warning: Failed to redirect legacy paths: ", redirectResult.error

    # Add to active cells
    manager.activeCells[nippelName] = nippel

    # Update last used timestamp
    nippel.lastUsed = now()

    echo "✅ Activated Nippel: ", nippelName
    return ok(true)

  except Exception as e:
    return err[bool]("Failed to activate Nippel: " & e.msg)

proc deactivateNippel*(manager: var NippelManager, nippelName: string): nipresult.Result[bool, string] =
  ## Deactivate a Nippel by exiting its namespaces (Task 8.2)
  try:
    # Check if active
    if nippelName notin manager.activeCells:
      return err[bool]("Nippel not active: " & nippelName)

    let nippel = manager.activeCells[nippelName]

    # Task 8.2: Exit namespaces if they exist
    if nippel.namespaceHandle.isSome:
      echo "🔄 Deactivating Nippel and exiting namespaces..."

      let nsHandle = nippel.namespaceHandle.get()

      # Exit namespaces
      let exitResult = ns.exitNamespace(nsHandle)
      if exitResult.isErr:
        echo "⚠️  Warning: Failed to exit namespaces cleanly: ", exitResult.error

      # Destroy namespaces
      let destroyResult = ns.destroyNamespaces(nsHandle)
      if destroyResult.isErr:
        echo "⚠️  Warning: Failed to destroy namespaces: ", destroyResult.error

      echo "✅ Exited namespaces"
    else:
      echo "ℹ️  Deactivating without namespace cleanup"

    # Remove from active cells
    manager.activeCells.del(nippelName)

    echo "✅ Deactivated Nippel: ", nippelName
    return ok(true)

  except Exception as e:
    return err[bool]("Failed to deactivate Nippel: " & e.msg)

proc isNippelActive*(manager: NippelManager, nippelName: string): bool =
  ## Check if a Nippel is currently active
  nippelName in manager.activeCells

proc getActiveNippels*(manager: NippelManager): seq[string] =
  ## Get list of currently active Nippels
  result = @[]
  for name in manager.activeCells.keys:
    result.add(name)

# =============================================================================
# Profile Management Operations (Task 8.1)
# =============================================================================

proc changeNippelProfile*(manager: var NippelManager, nippelName: string,
                          newProfile: SecurityProfile): nipresult.Result[bool, string] =
  ## Change a Nippel's security profile (Task 8.1)
  try:
    # Load the Nippel
    let cellRoot = manager.cellsRoot / nippelName
    if not dirExists(cellRoot):
      return err[bool]("Nippel not found: " & nippelName)

    let configPath = cellRoot / "cell.json"
    if not fileExists(configPath):
      return err[bool]("Nippel configuration not found: " & nippelName)

    # Parse existing configuration
    let config = parseJson(readFile(configPath))

    # Create Nippel object from config
    var nippel = Nippel(
      name: config["nippel"]["name"].getStr(),
      id: config["nippel"]["id"].getStr(),
      created: parse(config["nippel"]["created"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
      lastUsed: now(),
      profile: newProfile,  # Use new profile
      profileSettings: ProfileSettings(),  # Will be loaded below
      isolationLevel: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
      cellRoot: cellRoot,
      xdgDirs: XDGDirectories(
        dataHome: config["paths"]["data"].getStr(),
        configHome: config["paths"]["config"].getStr(),
        cacheHome: config["paths"]["cache"].getStr(),
        stateHome: config["paths"]["state"].getStr(),
        runtimeDir: config["paths"]["runtime"].getStr()
      ),
      namespaceHandle: none(NamespaceHandle),
      casEntries: @[],
      merkleRoot: config["storage"]["merkle_root"].getStr(),
      utcpAddress: UTCPAddress(),  # Will be parsed below
      packages: @[],
      metadata: newJObject(),
      version: config["nippel"]["version"].getStr()
    )

    # Load new profile settings
    let newSettings = manager.profileManager.loadProfile(newProfile)

    # Apply new profile
    let applyResult = applyProfile(nippel, newSettings)
    if applyResult.isErr:
      return err[bool]("Failed to apply new profile: " & applyResult.error)

    echo "✅ Changed Nippel profile: ", nippelName
    echo "   Old Profile: ", config["profile"]["type"].getStr()
    echo "   New Profile: ", newProfile

    return ok(true)

  except Exception as e:
    return err[bool]("Failed to change profile: " & e.msg)

proc customizeNippelProfile*(manager: var NippelManager, nippelName: string,
                             overrides: ProfileOverrides): nipresult.Result[bool, string] =
  ## Customize a Nippel's profile settings (Task 8.1)
  try:
    # Load the Nippel
    let cellRoot = manager.cellsRoot / nippelName
    if not dirExists(cellRoot):
      return err[bool]("Nippel not found: " & nippelName)

    let configPath = cellRoot / "cell.json"
    if not fileExists(configPath):
      return err[bool]("Nippel configuration not found: " & nippelName)

    # Parse existing configuration
    let config = parseJson(readFile(configPath))

    # Create Nippel object from config
    var nippel = Nippel(
      name: config["nippel"]["name"].getStr(),
      id: config["nippel"]["id"].getStr(),
      created: parse(config["nippel"]["created"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
      lastUsed: now(),
      profile: parseEnum[SecurityProfile](config["profile"]["type"].getStr()),
      profileSettings: ProfileSettings(),  # Will be loaded below
      isolationLevel: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
      cellRoot: cellRoot,
      xdgDirs: XDGDirectories(
        dataHome: config["paths"]["data"].getStr(),
        configHome: config["paths"]["config"].getStr(),
        cacheHome: config["paths"]["cache"].getStr(),
        stateHome: config["paths"]["state"].getStr(),
        runtimeDir: config["paths"]["runtime"].getStr()
      ),
      namespaceHandle: none(NamespaceHandle),
      casEntries: @[],
      merkleRoot: config["storage"]["merkle_root"].getStr(),
      utcpAddress: UTCPAddress(),
      packages: @[],
      metadata: newJObject(),
      version: config["nippel"]["version"].getStr()
    )

    # Load current profile settings
    nippel.profileSettings = manager.profileManager.loadProfile(nippel.profile)

    # Apply customizations
    let customizeResult = customizeProfile(nippel, overrides)
    if customizeResult.isErr:
      return err[bool]("Failed to customize profile: " & customizeResult.error)

    echo "✅ Customized Nippel profile: ", nippelName

    return ok(true)

  except Exception as e:
    return err[bool]("Failed to customize profile: " & e.msg)

# =============================================================================
# Nippel Information
# =============================================================================

proc listNippels*(manager: NippelManager): seq[NippelInfo] =
  ## List all available Nippels
  result = @[]

  if not dirExists(manager.cellsRoot):
    return result

  for kind, path in walkDir(manager.cellsRoot):
    if kind == pcDir:
      let configPath = path / "cell.json"

      if fileExists(configPath):
        try:
          let config = parseJson(readFile(configPath))
          let info = NippelInfo(
            name: config["nippel"]["name"].getStr(),
            profile: parseEnum[SecurityProfile](config["profile"]["type"].getStr()),
            isolation: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
            created: parse(config["nippel"]["created"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
            lastUsed: parse(config["nippel"]["lastUsed"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
            size: config["storage"]["total_size"].getInt(),
            packageCount: config["packages"].len
          )
          result.add(info)
        except:
          # Skip cells with invalid configuration
          discard

proc getNippel*(manager: NippelManager, name: string): Option[Nippel] =
  ## Get a Nippel by name (loads from disk if not active)
  ## Returns Some(nippel) if found, None if not found

  # Check if it's in active cells first
  if manager.activeCells.hasKey(name):
    return some(manager.activeCells[name])

  # Load from disk
  let nippelPath = manager.cellsRoot / name
  let configPath = nippelPath / "cell.json"

  if not fileExists(configPath):
    return none(Nippel)

  try:
    let config = parseJson(readFile(configPath))

    # Reconstruct Nippel object from config
    let nippel = Nippel(
      name: config["nippel"]["name"].getStr(),
      id: config["nippel"]["id"].getStr(),
      cellRoot: nippelPath,
      profile: parseEnum[SecurityProfile](config["profile"]["type"].getStr()),
      isolationLevel: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
      created: parse(config["nippel"]["created"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
      lastUsed: parse(config["nippel"]["lastUsed"].getStr(), "yyyy-MM-dd'T'HH:mm:sszzz"),
      profileSettings: ProfileSettings(
        isolationLevel: parseEnum[IsolationLevel](config["profile"]["isolation"].getStr()),
        desktopIntegration: config["profile"]["desktopIntegration"].getBool(),
        networkAccess: parseEnum[NetworkAccessLevel](config["profile"]["networkAccess"].getStr()),
        auditingEnabled: if config["profile"].hasKey("auditingEnabled"): config["profile"]["auditingEnabled"].getBool() else: false,
        resourceLimits: ResourceLimits(
          maxMemory: config["profile"]["resourceLimits"]["maxMemory"].getInt(),
          maxCPU: config["profile"]["resourceLimits"]["maxCPU"].getFloat()
        )
      ),
      xdgDirs: XDGDirectories(
        dataHome: nippelPath / ".local/share",
        configHome: nippelPath / ".config",
        cacheHome: nippelPath / ".cache",
        stateHome: nippelPath / ".local/state",
        runtimeDir: when defined(posix): "/run/user/" & $getuid() else: getTempDir()
      ),
      utcpAddress: UTCPAddress(
        scheme: config["utcp"]["scheme"].getStr(),
        host: config["utcp"]["host"].getStr(),
        resource: config["utcp"]["resource"].getStr(),
        port: if config["utcp"].hasKey("port"): config["utcp"]["port"].getInt() else: 0
      ),
      merkleRoot: if config.hasKey("merkleRoot"): config["merkleRoot"].getStr() else: "",
      casEntries: @[],
      packages: @[],
      namespaceHandle: none(NamespaceHandle),
      metadata: newJObject()
    )

    return some(nippel)
  except:
    return none(Nippel)

proc deleteNippel*(manager: var NippelManager, name: string): Result[bool, string] =
  ## Delete a Nippel completely
  ## Returns ok(true) on success, err(message) on failure

  let nippelPath = manager.cellsRoot / name

  if not dirExists(nippelPath):
    return err[bool]("Nippel not found: " & name)

  try:
    # Remove from active cells if present
    if manager.activeCells.hasKey(name):
      manager.activeCells.del(name)

    # Remove merkle tree if present
    if manager.merkleTrees.hasKey(name):
      manager.merkleTrees.del(name)

    # Remove directory
    removeDir(nippelPath)

    return ok(true)
  except Exception as e:
    return err[bool]("Failed to delete Nippel: " & e.msg)

# =============================================================================
# Exports
# =============================================================================

export SecurityProfile, IsolationLevel, NetworkAccessLevel
export ProfileSettings, ResourceLimits, XDGDirectories
export NamespaceHandle, CASEntry, PackageRef, UTCPAddress
export ErrorCode, NippelError, NippelManager
export NippelGlobalConfig, NippelInfo
export ProfileOverrides  # Task 8.1: Export ProfileOverrides for customization
# Note: Nippel type not exported due to name conflict with UTCPResourceType.Nippel
