## Manifest Parser - Format-Agnostic Package Manifest Parsing
##
## **Systems Engineering Approach:**
## - Strict-by-default validation with whitelist-based field controlpport.
## - Format-agnostic: KDL (human-friendly) and JSON (machine-friendly)
## - Semantic versioning enforcement (not just strings)
## - Platform/Architecture constraint awareness
## - UTCP protocol support for AI accessibility
## - Zero tolerance for contamination (unknown fields = rejection)
##
## **Core Philosophy:**
## The manifest is the contract. If it lies, we reject it.
## If it's ambiguous, we reject it. If it's incomplete, we reject it.

import std/[strutils, options, sets, json, sequtils, tables, algorithm]
import nimpak/kdl_parser
import nip/platform
import nip/xxhash

type
  # ============================================================================
  # Format Types
  # ============================================================================

  ManifestFormat* = enum
    ## Supported manifest formats (wire format)
    FormatKDL = "kdl"      ## Human-friendly KDL format
    FormatJSON = "json"    ## Machine-friendly JSON format
    FormatAuto = "auto"    ## Auto-detect from content

  FormatType* = enum
    ## Package format types (semantic meaning)
    NPK = "npk"           ## Nexus Package Kit (Standard distribution)
    NIP = "nip"           ## Nexus Installed Package (Local state)
    NEXTER = "nexter"     ## Nexus Container (Opaque runtime)

  # ============================================================================
  # Validation & Error Handling
  # ============================================================================

  ManifestValidationMode* = enum
    ## Validation strictness levels
    ValidationStrict   ## Reject unknown fields, enforce all constraints (DEFAULT)
    ValidationLenient  ## Warn on unknown fields, allow missing optional fields
    ValidationMinimal  ## Only validate required fields (unsafe, testing only)

  ManifestErrorCode* = enum
    ## Specific error codes for precise diagnostics
    InvalidFormat,        ## Syntax error in wire format
    MissingField,         ## Required field absent
    InvalidValue,         ## Field present but value invalid
    StrictViolation,      ## Unknown field detected (contamination)
    SemVerViolation,      ## Version string not valid semver
    SchemaError,          ## Structural schema violation
    HashMismatch,         ## Integrity hash mismatch
    PlatformIncompat,     ## Platform/arch constraint violation
    DependencyError       ## Dependency specification invalid

  ManifestError* = object of CatchableError
    ## Detailed error with context and suggestions
    code*: ManifestErrorCode
    field*: string        ## Field that caused the error
    line*: int            ## Line number (if available)
    context*: string      ## Human-readable context
    suggestions*: seq[string]  ## Actionable suggestions

  # ============================================================================
  # Core Manifest Types
  # ============================================================================

  PackageManifest* = object
    ## Complete package manifest (the contract)
    # Identity
    format*: FormatType
    name*: string
    version*: SemanticVersion  ## Parsed, not string
    description*: Option[string]
    homepage*: Option[string]
    license*: string

    # Dependencies
    dependencies*: seq[DependencySpec]
    buildDependencies*: seq[DependencySpec]
    optionalDependencies*: seq[DependencySpec]

    # Build configuration
    buildSystem*: Option[string]
    buildFlags*: seq[string]
    configureFlags*: seq[string]

    # Platform constraints (THE PHYSICAL WORLD)
    supportedOS*: seq[string]           ## e.g., ["linux", "freebsd"]
    supportedArchitectures*: seq[string] ## e.g., ["x86_64", "aarch64"]
    requiredCapabilities*: seq[string]   ## e.g., ["user_namespaces"]

    # Runtime configuration
    libc*: Option[string]
    allocator*: Option[string]

    # Integrity (cryptographic truth)
    buildHash*: string      ## BLAKE3 hash of build configuration
    sourceHash*: string     ## BLAKE3 hash of source
    artifactHash*: string   ## BLAKE3 hash of final artifact

    # Metadata
    author*: Option[string]
    timestamp*: Option[string]
    tags*: seq[string]
    maintainers*: seq[string]

    # UTCP support (AI accessibility)
    utcpEndpoint*: Option[string]  ## Remote query endpoint
    utcpVersion*: Option[string]   ## UTCP protocol version

    # System Integration
    files*: seq[FileSpec]
    users*: seq[UserSpec]
    groups*: seq[GroupSpec]
    services*: seq[ServiceSpec]

    # Security / Sandbox (NIP)
    sandbox*: Option[SandboxConfig]

    # Desktop Integration (NIP)
    desktop*: Option[DesktopIntegration]

  DesktopIntegration* = object
    ## Desktop environment integration
    displayName*: string       ## Human readable name (e.g. "Firefox Web Browser")
    icon*: Option[string]      ## Icon name or path
    categories*: seq[string]   ## Menu categories (e.g. "Network;WebBrowser")
    keywords*: seq[string]     ## Search keywords
    mimeTypes*: seq[string]    ## Supported MIME types
    terminal*: bool            ## Run in terminal?
    startupNotify*: bool       ## Support startup notification?
    startupWMClass*: Option[string] ## For window grouping (StartupWMClass)

  SandboxLevel* = enum
    SandboxStrict = "strict"      ## Maximum isolation (default)
    SandboxStandard = "standard"  ## Standard desktop app isolation
    SandboxRelaxed = "relaxed"    ## Minimal isolation (use with caution)
    SandboxNone = "none"          ## No isolation (requires user override)

  SandboxConfig* = object
    ## Sandboxing configuration for NIPs
    level*: SandboxLevel
    
    # Linux Specific
    seccompProfile*: Option[string]  ## "default", "strict", or custom path
    capabilities*: seq[string]       ## e.g. "CAP_NET_ADMIN" (usually to drop)
    namespaces*: seq[string]         ## e.g. "net", "ipc", "pid"
    
    # BSD Specific (OpenBSD/DragonflyBSD)
    pledge*: Option[string]          ## e.g. "stdio rpath wpath inet"
    unveil*: seq[string]             ## e.g. "/tmp:rwc"

  DependencySpec* = object
    ## Package dependency specification
    name*: string
    versionConstraint*: VersionConstraint
    optional*: bool
    features*: seq[string]

  FileSpec* = object
    ## File specification for installation
    path*: string
    hash*: string
    size*: int64
    permissions*: string # e.g. "755"

  UserSpec* = object
    ## System user specification
    name*: string
    uid*: Option[int]
    group*: string
    shell*: string
    home*: string

  GroupSpec* = object
    ## System group specification
    name*: string
    gid*: Option[int]

  ServiceSpec* = object
    ## System service specification
    name*: string
    content*: string # Content of the service file
    enabled*: bool

  SemanticVersion* = object
    ## Semantic version (major.minor.patch-prerelease+build)
    ## NOT just a string - this is a structured type
    major*: int
    minor*: int
    patch*: int
    prerelease*: string
    build*: string

  VersionConstraint* = object
    ## Version constraint specification
    operator*: VersionOperator
    version*: SemanticVersion

  VersionOperator* = enum
    ## Version comparison operators
    OpExact = "="      ## Exact version match
    OpGreater = ">"    ## Greater than
    OpGreaterEq = ">=" ## Greater than or equal
    OpLess = "<"       ## Less than
    OpLessEq = "<="    ## Less than or equal
    OpTilde = "~"      ## Compatible version (~1.2.3 = >=1.2.3 <1.3.0)
    OpCaret = "^"      ## Compatible version (^1.2.3 = >=1.2.3 <2.0.0)
    OpAny = "*"        ## Any version

  # ============================================================================
  # Validation Rules (The Enforcers)
  # ============================================================================

  ValidationRule* = object
    ## A validation rule that can be applied to manifest data
    name*: string
    description*: string
    ## Validate function receives parsed data and accumulates errors
    validate*: proc(data: JsonNode, errors: var seq[ManifestError]): bool

  ParserConfig* = object
    ## Parser configuration
    format*: FormatType
    wireFormat*: ManifestFormat  ## KDL or JSON
    strictMode*: bool
    allowedFields*: HashSet[string]  ## The Whitelist (The Bouncer)

  ManifestParser* = object
    ## Parser state and configuration
    config*: ParserConfig
    rules*: seq[ValidationRule]
    warnings*: seq[string]

# ============================================================================
# Known Fields (The Whitelist)
# ============================================================================

const BASE_ALLOWED_FIELDS = [
  # Identity
  "name", "version", "description", "homepage", "license", "author",
  # Dependencies
  "dependencies", "build_dependencies", "optional_dependencies",
  # Build
  "build_system", "build_flags", "configure_flags",
  # Platform
  "os", "arch", "supported_os", "supported_architectures", "required_capabilities",
  # Runtime
  "libc", "allocator",
  # Integrity
  "build_hash", "source_hash", "artifact_hash",
  # Metadata
  "timestamp", "tags", "maintainers",
  # UTCP
  "utcp_endpoint", "utcp_version"
].toHashSet()

const NPK_SPECIFIC_FIELDS = [
  "files", "install_scripts", "post_install"
].toHashSet()

const NIP_SPECIFIC_FIELDS = [
  "desktop", "permissions", "installed_path", "sandbox"
].toHashSet()

const NEXTER_SPECIFIC_FIELDS = [
  "container", "env", "entrypoint", "volumes"
].toHashSet()

const KNOWN_DEPENDENCY_FIELDS = [
  "name", "version", "optional", "features"
].toHashSet()

# Valid platform values
const VALID_OS = [
  "linux", "freebsd", "openbsd", "netbsd", "dragonfly",
  "macos", "windows", "android", "ios"
].toHashSet()

const VALID_ARCHITECTURES = [
  "x86_64", "aarch64", "armv8", "armv7", "armv6", "i686",
  "riscv64", "riscv32", "powerpc64", "powerpc64le",
  "s390x", "mips64", "mips64el"
].toHashSet()

# ============================================================================
# Format Detection
# ============================================================================

proc detectFormat*(content: string): ManifestFormat =
  ## Auto-detect manifest format from content
  let trimmed = content.strip()

  if trimmed.len > 0 and (trimmed[0] == '{' or trimmed[0] == '['):
    return FormatJSON

  # KDL typically starts with a node name
  if trimmed.len > 0 and trimmed[0] != '{':
    return FormatKDL

  raise newException(ManifestError, "Unable to detect manifest format")

# ============================================================================
# Semantic Versioning (NOT JUST STRINGS)
# ============================================================================

proc isValidSemVer*(v: string): bool =
  ## Quick check if string looks like semver (X.Y.Z)
  if v.len == 0: return false
  let parts = v.split({'.', '-', '+'})
  if parts.len < 3: return false

  # Ensure first three are numbers
  try:
    discard parseInt(parts[0])
    discard parseInt(parts[1])
    discard parseInt(parts[2])
    return true
  except ValueError:
    return false

proc parseSemanticVersion*(version: string): SemanticVersion =
  ## Parse semantic version string (major.minor.patch-prerelease+build)
  ## This is NOT just string validation - we parse into structured data

  if not isValidSemVer(version):
    raise newException(ManifestError,
      "Invalid semantic version: " & version & " (expected X.Y.Z)")

  var parts = version.split('-', maxsplit=1)
  var versionPart = parts[0]
  var prerelease = ""
  var build = ""

  if parts.len > 1:
    var prereleaseAndBuild = parts[1].split('+', maxsplit=1)
    prerelease = prereleaseAndBuild[0]
    if prereleaseAndBuild.len > 1:
      build = prereleaseAndBuild[1]
  else:
    # Check for build metadata without prerelease
    parts = versionPart.split('+', maxsplit=1)
    versionPart = parts[0]
    if parts.len > 1:
      build = parts[1]

  let versionNumbers = versionPart.split('.')
  if versionNumbers.len != 3:
    raise newException(ManifestError,
      "Invalid semantic version: " & version & " (expected major.minor.patch)")

  try:
    result = SemanticVersion(
      major: parseInt(versionNumbers[0]),
      minor: parseInt(versionNumbers[1]),
      patch: parseInt(versionNumbers[2]),
      prerelease: prerelease,
      build: build
    )
  except ValueError as e:
    raise newException(ManifestError,
      "Invalid semantic version numbers: " & version & " (" & e.msg & ")")

proc `$`*(v: SemanticVersion): string =
  ## Convert semantic version to string
  result = $v.major & "." & $v.minor & "." & $v.patch
  if v.prerelease.len > 0:
    result.add("-" & v.prerelease)
  if v.build.len > 0:
    result.add("+" & v.build)

proc compareVersions*(a, b: SemanticVersion): int =
  ## Compare two semantic versions (-1: a<b, 0: a==b, 1: a>b)
  ## Follows semver 2.0.0 specification

  # Compare major.minor.patch
  if a.major != b.major:
    return cmp(a.major, b.major)
  if a.minor != b.minor:
    return cmp(a.minor, b.minor)
  if a.patch != b.patch:
    return cmp(a.patch, b.patch)

  # Prerelease comparison
  if a.prerelease.len == 0 and b.prerelease.len > 0:
    return 1  # Release > prerelease
  if a.prerelease.len > 0 and b.prerelease.len == 0:
    return -1  # Prerelease < release
  if a.prerelease != b.prerelease:
    return cmp(a.prerelease, b.prerelease)

  # Build metadata is ignored in version precedence
  return 0

proc `<`*(a, b: SemanticVersion): bool = compareVersions(a, b) < 0
proc `<=`*(a, b: SemanticVersion): bool = compareVersions(a, b) <= 0
proc `==`*(a, b: SemanticVersion): bool = compareVersions(a, b) == 0
proc `>`*(a, b: SemanticVersion): bool = compareVersions(a, b) > 0
proc `>=`*(a, b: SemanticVersion): bool = compareVersions(a, b) >= 0


proc parseVersionConstraint*(constraint: string): VersionConstraint =
  ## Parse version constraint (e.g., ">=1.2.3", "~1.0.0", "^2.0.0")
  let trimmed = constraint.strip()

  if trimmed == "*":
    return VersionConstraint(operator: OpAny, version: SemanticVersion())

  var operator: VersionOperator
  var versionStr: string

  if trimmed.startsWith(">="):
    operator = OpGreaterEq
    versionStr = trimmed[2..^1].strip()
  elif trimmed.startsWith("<="):
    operator = OpLessEq
    versionStr = trimmed[2..^1].strip()
  elif trimmed.startsWith(">"):
    operator = OpGreater
    versionStr = trimmed[1..^1].strip()
  elif trimmed.startsWith("<"):
    operator = OpLess
    versionStr = trimmed[1..^1].strip()
  elif trimmed.startsWith("~"):
    operator = OpTilde
    versionStr = trimmed[1..^1].strip()
  elif trimmed.startsWith("^"):
    operator = OpCaret
    versionStr = trimmed[1..^1].strip()
  elif trimmed.startsWith("="):
    operator = OpExact
    versionStr = trimmed[1..^1].strip()
  else:
    # No operator means exact match
    operator = OpExact
    versionStr = trimmed

  let version = parseSemanticVersion(versionStr)
  result = VersionConstraint(operator: operator, version: version)

proc satisfiesConstraint*(version: SemanticVersion, constraint: VersionConstraint): bool =
  ## Check if a version satisfies a constraint
  case constraint.operator:
  of OpAny:
    return true
  of OpExact:
    return version == constraint.version
  of OpGreater:
    return version > constraint.version
  of OpGreaterEq:
    return version >= constraint.version
  of OpLess:
    return version < constraint.version
  of OpLessEq:
    return version <= constraint.version
  of OpTilde:
    # ~1.2.3 means >=1.2.3 <1.3.0
    if version < constraint.version:
      return false
    return version.major == constraint.version.major and
           version.minor == constraint.version.minor
  of OpCaret:
    # ^1.2.3 means >=1.2.3 <2.0.0
    if version < constraint.version:
      return false
    return version.major == constraint.version.major

proc parseSandboxLevel*(s: string): SandboxLevel =
  case s.toLowerAscii():
  of "strict": return SandboxStrict
  of "standard": return SandboxStandard
  of "relaxed": return SandboxRelaxed
  of "none": return SandboxNone
  else: raise newException(ManifestError, "Invalid sandbox level: " & s)

# ============================================================================
# Validation Rules (The Enforcers)
# ============================================================================

proc createStrictStructRule*(allowed: HashSet[string]): ValidationRule =
  ## The Bouncer: Kicks out any field not on the whitelist
  ## This is your primary defense against contamination
  return ValidationRule(
    name: "strict_whitelist",
    description: "Ensures only authorized fields are present",
    validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
      var valid = true
      for key in data.keys:
        if key notin allowed:
          valid = false
          errors.add(ManifestError(
            code: StrictViolation,
            field: key,
            context: "Unauthorized field found: '" & key & "'",
            suggestions: @[
              "Remove the field",
              "Check spelling against spec",
              "This field may be format-specific"          ]
          ))
      return valid
  )

proc createSemVerRule*(fieldName: string): ValidationRule =
  ## Enforce semantic versioning on a field
  return ValidationRule(
    name: "semver_" & fieldName,
    description: "Field '" & fieldName & "' must be valid SemVer (X.Y.Z)",
    validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
      if fieldName notin data: return true  # Required check handles missing

      let val = data[fieldName].getStr()
      if not isValidSemVer(val):
        errors.add(ManifestError(
          code: SemVerViolation,
          field: fieldName,
          context: "'" & val & "' is not a valid SemVer",
          suggestions: @[
            "Use format X.Y.Z (e.g., 1.0.0)",
            "Prerelease: 1.0.0-alpha",
            "Build metadata: 1.0.0+20130313144700"
          ]
        ))
        return false
      return true
  )

proc createPlatformConstraintRule*(): ValidationRule =
  ## Validate OS and Architecture constraints
  return ValidationRule(
    name: "platform_constraints",
    description: "Validates OS and Architecture targets",
    validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
      var valid = true

      # Validate OS field
      if "os" in data or "supported_os" in data:
        let osField = if "os" in data: "os" else: "supported_os"
        let osNode = data[osField]

        if osNode.kind != JArray:
          errors.add(ManifestError(
            code: InvalidValue,
            field: osField,
            context: "OS field must be an array",
            suggestions: @["Use array format: [\"linux\", \"freebsd\"]"]
          ))
          valid = false
        elif osNode.len == 0:
          errors.add(ManifestError(
            code: InvalidValue,
            field: osField,
            context: "OS array cannot be empty",
            suggestions: @["Specify at least one OS"]
          ))
          valid = false
        else:
          # Validate each OS value
          for osVal in osNode:
            let os = osVal.getStr()
            if os notin VALID_OS:
              errors.add(ManifestError(
                code: PlatformIncompat,
                field: osField,
                context: "Invalid OS: " & os,
                suggestions: @["Valid OS: " & $VALID_OS]
              ))
              valid = false

      # Validate Architecture field
      if "arch" in data or "supported_architectures" in data:
        let archField = if "arch" in data: "arch" else: "supported_architectures"
        let archNode = data[archField]

        if archNode.kind != JArray:
          errors.add(ManifestError(
            code: InvalidValue,
            field: archField,
            context: "Architecture field must be an array",
            suggestions: @["Use array format: [\"x86_64\", \"aarch64\"]"]
          ))
          valid = false
        elif archNode.len == 0:
          errors.add(ManifestError(
            code: InvalidValue,
            field: archField,
            context: "Architecture array cannot be empty",
            suggestions: @["Specify at least one architecture"]
          ))
          valid = false
        else:
          # Validate each architecture value
          for archVal in archNode:
            let arch = archVal.getStr()
            if arch notin VALID_ARCHITECTURES:
              errors.add(ManifestError(
                code: PlatformIncompat,
                field: archField,
                context: "Invalid architecture: " & arch,
                suggestions: @["Valid architectures: " & $VALID_ARCHITECTURES]
              ))
              valid = false

      return valid
  )

proc createRequiredFieldsRule*(fields: seq[string]): ValidationRule =
  ## Ensure required fields are present
  return ValidationRule(
    name: "required_fields",
    description: "Ensures all required fields are present",
    validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
      var valid = true
      for field in fields:
        if field notin data:
          errors.add(ManifestError(
            code: MissingField,
            field: field,
            context: "Required field '" & field & "' is missing",
            suggestions: @["Add the field to the manifest"]
          ))
          valid = false
      return valid
  )

proc createDependencyRule*(): ValidationRule =
  ## Validate dependency specifications
  return ValidationRule(
    name: "dependencies",
    description: "Validates dependency specifications",
    validate: proc(data: JsonNode, errors: var seq[ManifestError]): bool =
      var valid = true

      for depField in ["dependencies", "build_dependencies", "optional_dependencies"]:
        if depField notin data: continue

        let deps = data[depField]
        if deps.kind != JArray:
          errors.add(ManifestError(
            code: SchemaError,
            field: depField,
            context: "Dependencies must be an array",
            suggestions: @["Use array format"]
          ))
          valid = false
          continue

        for dep in deps:
          if dep.kind != JObject:
            errors.add(ManifestError(
              code: SchemaError,
              field: depField,
              context: "Each dependency must be an object",
              suggestions: @["Use object format: {\"name\": \"pkg\", \"version\": \">=1.0.0\"}"]
            ))
            valid = false
            continue

          # Check required dependency fields
          if "name" notin dep:
            errors.add(ManifestError(
              code: MissingField,
              field: depField & ".name",
              context: "Dependency missing 'name' field",
              suggestions: @["Add name field"]
            ))
            valid = false

          # Validate version constraint if present
          if "version" in dep:
            let versionStr = dep["version"].getStr()
            try:
              discard parseVersionConstraint(versionStr)
            except ManifestError as e:
              errors.add(ManifestError(
                code: DependencyError,
                field: depField & ".version",
                context: "Invalid version constraint: " & versionStr,
                suggestions: @[
                  "Use valid constraint: >=1.0.0, ~1.2.0, ^2.0.0",
                  e.msg
                ]
              ))
              valid = false

      return valid
  )

# ============================================================================
# Parser Construction
# ============================================================================

proc newManifestParser*(format: FormatType,
                        wireFormat: ManifestFormat = FormatAuto,
                        strict: bool = true): ManifestParser =
  ## Create a new manifest parser with format-specific rules
  var parser = ManifestParser(
    config: ParserConfig(
      format: format,
      wireFormat: wireFormat,
      strictMode: strict,
      allowedFields: BASE_ALLOWED_FIELDS
    ),
    rules: @[],
    warnings: @[]
  )

  # Add format-specific allowed fields
  case format:
  of NPK:
    for field in NPK_SPECIFIC_FIELDS:
      parser.config.allowedFields.incl(field)
  of NIP:
    for field in NIP_SPECIFIC_FIELDS:
      parser.config.allowedFields.incl(field)
  of NEXTER:
    for field in NEXTER_SPECIFIC_FIELDS:
      parser.config.allowedFields.incl(field)

  # Add common validation rules
  parser.rules.add(createRequiredFieldsRule(@["name", "version", "license"]))
  parser.rules.add(createSemVerRule("version"))
  parser.rules.add(createPlatformConstraintRule())
  parser.rules.add(createDependencyRule())

  # Add strict mode rule (The Bouncer) - LAST
  if strict:
    parser.rules.add(createStrictStructRule(parser.config.allowedFields))

  return parser

# ============================================================================
# Platform Validation
# ============================================================================

proc checkPlatformCompatibility*(manifest: PackageManifest,
                                  caps: PlatformCapabilities): bool =
  ## Check if package is compatible with current platform

  # Check OS
  if manifest.supportedOS.len > 0:
    let currentOS = $caps.osType
    if currentOS notin manifest.supportedOS:
      return false

  # Check required capabilities
  for cap in manifest.requiredCapabilities:
    case cap:
    of "user_namespaces":
      if not caps.hasUserNamespaces:
        return false
    of "jails":
      if not caps.hasJails:
        return false
    of "unveil":
      if not caps.hasUnveil:
        return false
    else:
      # Unknown capability - be conservative
      return false

  return true


# ============================================================================
# JSON Parsing (Machine-Friendly)
# ============================================================================

proc parseManifestFromJSON*(content: string, parser: var ManifestParser): PackageManifest =
  ## Parse package manifest from JSON format
  ## This is the machine-friendly format for automated systems

  let jsonNode = try:
    parseJson(content)
  except JsonParsingError as e:
    raise newException(ManifestError, "JSON parse error: " & e.msg)

  if jsonNode.kind != JObject:
    raise newException(ManifestError, "JSON manifest must be an object")

  # Run validation rules
  var errors: seq[ManifestError] = @[]
  for rule in parser.rules:
    discard rule.validate(jsonNode, errors)

  if errors.len > 0:
    # Collect all errors into one exception
    var msg = "Manifest validation failed:\n"
    for err in errors:
      msg.add("  [" & $err.code & "] " & err.context & "\n")
      for suggestion in err.suggestions:
        msg.add("    → " & suggestion & "\n")
    raise newException(ManifestError, msg)

  # Extract manifest data (safe after validation)
  var manifest = PackageManifest(
    format: parser.config.format,
    name: jsonNode["name"].getStr(),
    version: parseSemanticVersion(jsonNode["version"].getStr()),
    license: jsonNode["license"].getStr()
  )

  # Optional fields
  if jsonNode.hasKey("description"):
    manifest.description = some(jsonNode["description"].getStr())

  if jsonNode.hasKey("homepage"):
    manifest.homepage = some(jsonNode["homepage"].getStr())

  if jsonNode.hasKey("author"):
    manifest.author = some(jsonNode["author"].getStr())

  if jsonNode.hasKey("timestamp"):
    manifest.timestamp = some(jsonNode["timestamp"].getStr())

  if jsonNode.hasKey("libc"):
    manifest.libc = some(jsonNode["libc"].getStr())

  if jsonNode.hasKey("allocator"):
    manifest.allocator = some(jsonNode["allocator"].getStr())

  if jsonNode.hasKey("build_system"):
    manifest.buildSystem = some(jsonNode["build_system"].getStr())

  # Integrity hashes
  if jsonNode.hasKey("build_hash"):
    manifest.buildHash = jsonNode["build_hash"].getStr()

  if jsonNode.hasKey("source_hash"):
    manifest.sourceHash = jsonNode["source_hash"].getStr()

  if jsonNode.hasKey("artifact_hash"):
    manifest.artifactHash = jsonNode["artifact_hash"].getStr()

  # UTCP support
  if jsonNode.hasKey("utcp_endpoint"):
    manifest.utcpEndpoint = some(jsonNode["utcp_endpoint"].getStr())

  if jsonNode.hasKey("utcp_version"):
    manifest.utcpVersion = some(jsonNode["utcp_version"].getStr())

  # Platform constraints
  if jsonNode.hasKey("os") or jsonNode.hasKey("supported_os"):
    let osField = if jsonNode.hasKey("os"): "os" else: "supported_os"
    for os in jsonNode[osField]:
      manifest.supportedOS.add(os.getStr())

  if jsonNode.hasKey("arch") or jsonNode.hasKey("supported_architectures"):
    let archField = if jsonNode.hasKey("arch"): "arch" else: "supported_architectures"
    for arch in jsonNode[archField]:
      manifest.supportedArchitectures.add(arch.getStr())

  if jsonNode.hasKey("required_capabilities"):
    for cap in jsonNode["required_capabilities"]:
      manifest.requiredCapabilities.add(cap.getStr())

  # Dependencies
  if jsonNode.hasKey("dependencies"):
    for dep in jsonNode["dependencies"]:
      var depSpec = DependencySpec(
        name: dep["name"].getStr(),
        optional: false
      )
      if dep.hasKey("version"):
        depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
      if dep.hasKey("optional"):
        depSpec.optional = dep["optional"].getBool()
      if dep.hasKey("features"):
        for feature in dep["features"]:
          depSpec.features.add(feature.getStr())
      manifest.dependencies.add(depSpec)

  if jsonNode.hasKey("build_dependencies"):
    for dep in jsonNode["build_dependencies"]:
      var depSpec = DependencySpec(
        name: dep["name"].getStr(),
        optional: false
      )
      if dep.hasKey("version"):
        depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
      manifest.buildDependencies.add(depSpec)

  if jsonNode.hasKey("optional_dependencies"):
    for dep in jsonNode["optional_dependencies"]:
      var depSpec = DependencySpec(
        name: dep["name"].getStr(),
        optional: true
      )
      if dep.hasKey("version"):
        depSpec.versionConstraint = parseVersionConstraint(dep["version"].getStr())
      manifest.optionalDependencies.add(depSpec)

  # Build configuration
  if jsonNode.hasKey("build_flags"):
    for flag in jsonNode["build_flags"]:
      manifest.buildFlags.add(flag.getStr())

  if jsonNode.hasKey("configure_flags"):
    for flag in jsonNode["configure_flags"]:
      manifest.configureFlags.add(flag.getStr())

  # Metadata
  if jsonNode.hasKey("tags"):
    for tag in jsonNode["tags"]:
      manifest.tags.add(tag.getStr())

  if jsonNode.hasKey("maintainers"):
    for maintainer in jsonNode["maintainers"]:
      manifest.maintainers.add(maintainer.getStr())

  # System Integration
  if jsonNode.hasKey("files"):
    for file in jsonNode["files"]:
      manifest.files.add(FileSpec(
        path: file["path"].getStr(),
        hash: file["hash"].getStr(),
        size: file["size"].getBiggestInt(),
        permissions: file.getOrDefault("permissions").getStr("644")
      ))

  if jsonNode.hasKey("users"):
    for user in jsonNode["users"]:
      var userSpec = UserSpec(
        name: user["name"].getStr(),
        group: user.getOrDefault("group").getStr(user["name"].getStr()),
        shell: user.getOrDefault("shell").getStr("/bin/false"),
        home: user.getOrDefault("home").getStr("/var/empty")
      )
      if user.hasKey("uid"):
        userSpec.uid = some(user["uid"].getInt())
      manifest.users.add(userSpec)

  if jsonNode.hasKey("groups"):
    for group in jsonNode["groups"]:
      var groupSpec = GroupSpec(name: group["name"].getStr())
      if group.hasKey("gid"):
        groupSpec.gid = some(group["gid"].getInt())
      manifest.groups.add(groupSpec)

  if jsonNode.hasKey("services"):
    for service in jsonNode["services"]:
      manifest.services.add(ServiceSpec(
        name: service["name"].getStr(),
        content: service.getOrDefault("content").getStr(""),
        enabled: service.getOrDefault("enabled").getBool(true)
      ))

  # Security / Sandbox
  if jsonNode.hasKey("sandbox"):
    let sbNode = jsonNode["sandbox"]
    var config = SandboxConfig(
      level: parseSandboxLevel(sbNode.getOrDefault("level").getStr("strict"))
    )
    
    # Linux
    if sbNode.hasKey("linux"):
      let linuxNode = sbNode["linux"]
      if linuxNode.hasKey("seccomp"):
        config.seccompProfile = some(linuxNode["seccomp"].getStr())
      
      if linuxNode.hasKey("capabilities"):
        for cap in linuxNode["capabilities"]:
          config.capabilities.add(cap.getStr())
          
      if linuxNode.hasKey("namespaces"):
        for ns in linuxNode["namespaces"]:
          config.namespaces.add(ns.getStr())
          
    # BSD
    if sbNode.hasKey("bsd"):
      let bsdNode = sbNode["bsd"]
      if bsdNode.hasKey("pledge"):
        config.pledge = some(bsdNode["pledge"].getStr())
        
      if bsdNode.hasKey("unveil"):
        for path in bsdNode["unveil"]:
          config.unveil.add(path.getStr())
          
    manifest.sandbox = some(config)

  # Desktop Integration
  if jsonNode.hasKey("desktop"):
    let dtNode = jsonNode["desktop"]
    var dt = DesktopIntegration(
      displayName: dtNode.getOrDefault("display_name").getStr(manifest.name),
      terminal: dtNode.getOrDefault("terminal").getBool(false),
      startupNotify: dtNode.getOrDefault("startup_notify").getBool(true)
    )
    
    if dtNode.hasKey("icon"):
      dt.icon = some(dtNode["icon"].getStr())
      
    if dtNode.hasKey("startup_wm_class"):
      dt.startupWMClass = some(dtNode["startup_wm_class"].getStr())
      
    if dtNode.hasKey("categories"):
      for cat in dtNode["categories"]:
        dt.categories.add(cat.getStr())
        
    if dtNode.hasKey("keywords"):
      for kw in dtNode["keywords"]:
        dt.keywords.add(kw.getStr())
        
    if dtNode.hasKey("mime_types"):
      for mt in dtNode["mime_types"]:
        dt.mimeTypes.add(mt.getStr())
        
    manifest.desktop = some(dt)

  return manifest

# ============================================================================
# KDL Parsing (Human-Friendly) - NATIVE IMPLEMENTATION
# ============================================================================

proc parseManifestFromKDL*(content: string, parser: var ManifestParser): PackageManifest =
  ## Parse package manifest from KDL format using NATIVE KDL structures
  ## No JSON conversion - direct KDL parsing for maximum efficiency

  let doc = parseKdlString(content)

  # Find package node
  let packageNode = doc.findNode("package")
  if packageNode.isNone:
    raise newException(ManifestError, "Missing 'package' node in KDL manifest")

  let pkg = packageNode.get()

  # Track seen fields for strict validation
  var seenFields: HashSet[string]

  # Initialize manifest
  var manifest = PackageManifest(format: parser.config.format)

  # Extract name from first argument (REQUIRED)
  if pkg.args.len > 0:
    manifest.name = pkg.getArgString(0)
    seenFields.incl("name")
  else:
    raise newException(ManifestError, "Missing package name")

  # Extract properties (NATIVE KDL)
  # Extract properties (NATIVE KDL)
  if pkg.hasProp("version"):
    let versionStr = pkg.getPropString("version")
    manifest.version = parseSemanticVersion(versionStr)
    seenFields.incl("version")
  else:
    # Check for child node "version"
    let verNode = pkg.findChild("version")
    if verNode.isSome:
      let versionStr = verNode.get().getArgString(0)
      manifest.version = parseSemanticVersion(versionStr)
      seenFields.incl("version")
    else:
      raise newException(ManifestError, "Missing package version")

  if pkg.hasProp("license"):
    manifest.license = pkg.getPropString("license")
    seenFields.incl("license")
  else:
    # Check for child node "license"
    let licNode = pkg.findChild("license")
    if licNode.isSome:
      manifest.license = licNode.get().getArgString(0)
      seenFields.incl("license")
    else:
      raise newException(ManifestError, "Missing package license")

  # Optional properties
  if pkg.hasProp("description"):
    manifest.description = some(pkg.getPropString("description"))
    seenFields.incl("description")

  if pkg.hasProp("homepage"):
    manifest.homepage = some(pkg.getPropString("homepage"))
    seenFields.incl("homepage")

  if pkg.hasProp("author"):
    manifest.author = some(pkg.getPropString("author"))
    seenFields.incl("author")

  if pkg.hasProp("timestamp"):
    manifest.timestamp = some(pkg.getPropString("timestamp"))
    seenFields.incl("timestamp")

  if pkg.hasProp("libc"):
    manifest.libc = some(pkg.getPropString("libc"))
    seenFields.incl("libc")

  if pkg.hasProp("allocator"):
    manifest.allocator = some(pkg.getPropString("allocator"))
    seenFields.incl("allocator")

  if pkg.hasProp("build_system"):
    manifest.buildSystem = some(pkg.getPropString("build_system"))
    seenFields.incl("build_system")

  # Integrity hashes
  if pkg.hasProp("build_hash"):
    manifest.buildHash = pkg.getPropString("build_hash")
    seenFields.incl("build_hash")

  if pkg.hasProp("source_hash"):
    manifest.sourceHash = pkg.getPropString("source_hash")
    seenFields.incl("source_hash")

  if pkg.hasProp("artifact_hash"):
    manifest.artifactHash = pkg.getPropString("artifact_hash")
    seenFields.incl("artifact_hash")

  # UTCP support
  if pkg.hasProp("utcp_endpoint"):
    manifest.utcpEndpoint = some(pkg.getPropString("utcp_endpoint"))
    seenFields.incl("utcp_endpoint")

  if pkg.hasProp("utcp_version"):
    manifest.utcpVersion = some(pkg.getPropString("utcp_version"))
    seenFields.incl("utcp_version")

  # Parse child nodes (NATIVE KDL)
  for child in pkg.children:
    seenFields.incl(child.name)

    case child.name:
    of "dependencies":
      for dep in child.children:
        if dep.args.len == 0:
          raise newException(ManifestError, "Dependency missing name")

        var depSpec = DependencySpec(
          name: dep.getArgString(0),
          optional: false
        )

        if dep.hasProp("version"):
          depSpec.versionConstraint = parseVersionConstraint(dep.getPropString("version"))

        if dep.hasProp("optional"):
          depSpec.optional = dep.getPropBool("optional")

        if dep.hasProp("features"):
          let featuresStr = dep.getPropString("features")
          depSpec.features = featuresStr.split(',').mapIt(it.strip())

        manifest.dependencies.add(depSpec)

    of "build_dependencies":
      for dep in child.children:
        if dep.args.len == 0:
          raise newException(ManifestError, "Build dependency missing name")

        var depSpec = DependencySpec(
          name: dep.getArgString(0),
          optional: false
        )

        if dep.hasProp("version"):
          depSpec.versionConstraint = parseVersionConstraint(dep.getPropString("version"))

        manifest.buildDependencies.add(depSpec)

    of "optional_dependencies":
      for dep in child.children:
        if dep.args.len == 0:
          raise newException(ManifestError, "Optional dependency missing name")

        var depSpec = DependencySpec(
          name: dep.getArgString(0),
          optional: true
        )

        if dep.hasProp("version"):
          depSpec.versionConstraint = parseVersionConstraint(dep.getPropString("version"))

        manifest.optionalDependencies.add(depSpec)

    of "build_flags":
      for flag in child.children:
        if flag.args.len > 0:
          manifest.buildFlags.add(flag.getArgString(0))

    of "configure_flags":
      for flag in child.children:
        if flag.args.len > 0:
          manifest.configureFlags.add(flag.getArgString(0))

    of "os", "supported_os":
      for os in child.children:
        if os.args.len > 0:
          let osStr = os.getArgString(0)
          if parser.config.strictMode and osStr notin VALID_OS:
            raise newException(ManifestError, "Invalid OS: " & osStr)
          manifest.supportedOS.add(osStr)

    of "arch", "supported_architectures":
      for arch in child.children:
        if arch.args.len > 0:
          let archStr = arch.getArgString(0)
          if parser.config.strictMode and archStr notin VALID_ARCHITECTURES:
            raise newException(ManifestError, "Invalid architecture: " & archStr)
          manifest.supportedArchitectures.add(archStr)

    of "required_capabilities":
      for cap in child.children:
        if cap.args.len > 0:
          manifest.requiredCapabilities.add(cap.getArgString(0))

    of "tags":
      for tag in child.children:
        if tag.args.len > 0:
          manifest.tags.add(tag.getArgString(0))

    of "maintainers":
      for maintainer in child.children:
        if maintainer.args.len > 0:
          manifest.maintainers.add(maintainer.getArgString(0))

    of "files":
      for file in child.children:
        if file.args.len == 0:
          raise newException(ManifestError, "File missing path")
        
        var fileSpec = FileSpec(
          path: file.getArgString(0),
          hash: file.getPropString("hash"),
          size: file.getPropInt("size"),
          permissions: file.getPropString("permissions", "644")
        )
        manifest.files.add(fileSpec)

    of "users":
      for user in child.children:
        if user.args.len == 0:
          raise newException(ManifestError, "User missing name")
        
        var userSpec = UserSpec(
          name: user.getArgString(0),
          group: user.getPropString("group", user.getArgString(0)),
          shell: user.getPropString("shell", "/bin/false"),
          home: user.getPropString("home", "/var/empty")
        )
        if user.hasProp("uid"):
          userSpec.uid = some(user.getPropInt("uid").int)
        
        manifest.users.add(userSpec)

    of "groups":
      for group in child.children:
        if group.args.len == 0:
          raise newException(ManifestError, "Group missing name")
        
        var groupSpec = GroupSpec(name: group.getArgString(0))
        if group.hasProp("gid"):
          groupSpec.gid = some(group.getPropInt("gid").int)
        
        manifest.groups.add(groupSpec)

    of "services":
      for service in child.children:
        if service.args.len == 0:
          raise newException(ManifestError, "Service missing name")
        
        var serviceSpec = ServiceSpec(
          name: service.getArgString(0),
          content: service.getPropString("content", ""),
          enabled: service.getPropBool("enabled", true)
        )
        manifest.services.add(serviceSpec)

        manifest.services.add(serviceSpec)

    of "sandbox":
      var config = SandboxConfig(
        level: parseSandboxLevel(child.getPropString("level", "strict"))
      )
      
      for sbChild in child.children:
        case sbChild.name:
        of "linux":
          if sbChild.hasProp("seccomp"):
            config.seccompProfile = some(sbChild.getPropString("seccomp"))
            
          for linuxChild in sbChild.children:
            case linuxChild.name:
            of "capabilities":
              for cap in linuxChild.args:
                config.capabilities.add(cap.getString())
            of "namespaces":
              for ns in linuxChild.args:
                config.namespaces.add(ns.getString())
        
        of "bsd":
          if sbChild.hasProp("pledge"):
            config.pledge = some(sbChild.getPropString("pledge"))
            
          for bsdChild in sbChild.children:
            case bsdChild.name:
            of "unveil":
              for path in bsdChild.args:
                config.unveil.add(path.getString())
      
      manifest.sandbox = some(config)

    of "desktop":
      var dt = DesktopIntegration(
        displayName: child.getPropString("display_name", manifest.name),
        terminal: child.getPropBool("terminal", false),
        startupNotify: child.getPropBool("startup_notify", true)
      )
      
      if child.hasProp("icon"):
        dt.icon = some(child.getPropString("icon"))
        
      if child.hasProp("startup_wm_class"):
        dt.startupWMClass = some(child.getPropString("startup_wm_class"))
        
      for dtChild in child.children:
        case dtChild.name:
        of "categories":
          for cat in dtChild.args:
            dt.categories.add(cat.getString())
        of "keywords":
          for kw in dtChild.args:
            dt.keywords.add(kw.getString())
        of "mime_types":
          for mt in dtChild.args:
            dt.mimeTypes.add(mt.getString())
            
      manifest.desktop = some(dt)

    else:
      if parser.config.strictMode and child.name notin parser.config.allowedFields:
        raise newException(ManifestError, "Unknown field: " & child.name)
      else:
        parser.warnings.add("Unknown field: " & child.name)

  # Strict mode: check for unknown properties
  if parser.config.strictMode:
    for key in keys(pkg.props):
      if key notin parser.config.allowedFields:
        raise newException(ManifestError, "Unknown property: " & key)

  return manifest

# ============================================================================
# High-Level API
# ============================================================================

proc parseManifest*(content: string,
                    format: FormatType = NPK,
                    wireFormat: ManifestFormat = FormatAuto,
                    validationMode: ManifestValidationMode = ValidationStrict): PackageManifest =
  ## Parse package manifest from string (auto-detects wire format)
  var parser = newManifestParser(format, wireFormat, validationMode == ValidationStrict)

  let actualFormat = if wireFormat == FormatAuto:
    detectFormat(content)
  else:
    wireFormat

  case actualFormat:
  of FormatKDL:
    return parseManifestFromKDL(content, parser)
  of FormatJSON:
    return parseManifestFromJSON(content, parser)
  of FormatAuto:
    raise newException(ManifestError, "Format detection failed")

proc parseManifestFile*(path: string,
                        format: FormatType = NPK,
                        wireFormat: ManifestFormat = FormatAuto,
                        validationMode: ManifestValidationMode = ValidationStrict): PackageManifest =
  ## Parse package manifest from file
  let content = readFile(path)
  return parseManifest(content, format, wireFormat, validationMode)

proc validateManifest*(manifest: PackageManifest): seq[string] =
  ## Validate manifest and return list of issues (empty if valid)
  result = @[]

  # Validate name
  if manifest.name.len == 0:
    result.add("Package name cannot be empty")

  # Version is already validated during parsing (it's a SemanticVersion)

  # Validate license
  if manifest.license.len == 0:
    result.add("License cannot be empty")

  # Platform compatibility is checked separately via checkPlatformCompatibility

# ============================================================================
# Serialization
# ============================================================================

proc serializeManifestToJSON*(manifest: PackageManifest): string =
  ## Serialize manifest to JSON format (machine-friendly)
  var jsonObj = %* {
    "name": manifest.name,
    "version": $manifest.version,
    "license": manifest.license
  }

  if manifest.description.isSome:
    jsonObj["description"] = %manifest.description.get()

  if manifest.homepage.isSome:
    jsonObj["homepage"] = %manifest.homepage.get()

  if manifest.author.isSome:
    jsonObj["author"] = %manifest.author.get()

  if manifest.timestamp.isSome:
    jsonObj["timestamp"] = %manifest.timestamp.get()

  if manifest.libc.isSome:
    jsonObj["libc"] = %manifest.libc.get()

  if manifest.allocator.isSome:
    jsonObj["allocator"] = %manifest.allocator.get()

  if manifest.buildSystem.isSome:
    jsonObj["build_system"] = %manifest.buildSystem.get()

  # Integrity
  if manifest.buildHash.len > 0:
    jsonObj["build_hash"] = %manifest.buildHash

  if manifest.sourceHash.len > 0:
    jsonObj["source_hash"] = %manifest.sourceHash

  if manifest.artifactHash.len > 0:
    jsonObj["artifact_hash"] = %manifest.artifactHash

  # UTCP
  if manifest.utcpEndpoint.isSome:
    jsonObj["utcp_endpoint"] = %manifest.utcpEndpoint.get()

  if manifest.utcpVersion.isSome:
    jsonObj["utcp_version"] = %manifest.utcpVersion.get()

  # Platform
  if manifest.supportedOS.len > 0:
    jsonObj["supported_os"] = %manifest.supportedOS

  if manifest.supportedArchitectures.len > 0:
    jsonObj["supported_architectures"] = %manifest.supportedArchitectures

  if manifest.requiredCapabilities.len > 0:
    jsonObj["required_capabilities"] = %manifest.requiredCapabilities

  # Dependencies
  if manifest.dependencies.len > 0:
    var deps = newJArray()
    for dep in manifest.dependencies:
      var depObj = %* {"name": dep.name}
      if dep.versionConstraint.operator != OpAny:
        depObj["version"] = %($dep.versionConstraint.operator & $dep.versionConstraint.version)
      if dep.optional:
        depObj["optional"] = %true
      if dep.features.len > 0:
        depObj["features"] = %dep.features
      deps.add(depObj)
    jsonObj["dependencies"] = deps
  
  # System Integration
  if manifest.files.len > 0:
    var files = newJArray()
    for file in manifest.files:
      files.add(%*{
        "path": file.path,
        "hash": file.hash,
        "size": file.size,
        "permissions": file.permissions
      })
    jsonObj["files"] = files

  if manifest.users.len > 0:
    var users = newJArray()
    for user in manifest.users:
      var userObj = %*{
        "name": user.name,
        "group": user.group,
        "shell": user.shell,
        "home": user.home
      }
      if user.uid.isSome:
        userObj["uid"] = %user.uid.get()
      users.add(userObj)
    jsonObj["users"] = users

  if manifest.groups.len > 0:
    var groups = newJArray()
    for group in manifest.groups:
      var groupObj = %*{"name": group.name}
      if group.gid.isSome:
        groupObj["gid"] = %group.gid.get()
      groups.add(groupObj)
    jsonObj["groups"] = groups

  if manifest.services.len > 0:
    var services = newJArray()
    for service in manifest.services:
      services.add(%*{
        "name": service.name,
        "content": service.content,
        "enabled": service.enabled
      })
    jsonObj["services"] = services

  # Security / Sandbox
  if manifest.sandbox.isSome:
    let sb = manifest.sandbox.get()
    var sbObj = %*{"level": $sb.level}
    
    var linuxObj = newJObject()
    if sb.seccompProfile.isSome:
      linuxObj["seccomp"] = %sb.seccompProfile.get()
    if sb.capabilities.len > 0:
      linuxObj["capabilities"] = %sb.capabilities
    if sb.namespaces.len > 0:
      linuxObj["namespaces"] = %sb.namespaces
    if linuxObj.len > 0:
      sbObj["linux"] = linuxObj
      
    var bsdObj = newJObject()
    if sb.pledge.isSome:
      bsdObj["pledge"] = %sb.pledge.get()
    if sb.unveil.len > 0:
      bsdObj["unveil"] = %sb.unveil
    if bsdObj.len > 0:
      sbObj["bsd"] = bsdObj
      
    jsonObj["sandbox"] = sbObj

  # Desktop Integration
  if manifest.desktop.isSome:
    let dt = manifest.desktop.get()
    var dtObj = %*{
      "display_name": dt.displayName,
      "terminal": dt.terminal,
      "startup_notify": dt.startupNotify
    }
    
    if dt.icon.isSome:
      dtObj["icon"] = %dt.icon.get()
    if dt.startupWMClass.isSome:
      dtObj["startup_wm_class"] = %dt.startupWMClass.get()
    if dt.categories.len > 0:
      dtObj["categories"] = %dt.categories
    if dt.keywords.len > 0:
      dtObj["keywords"] = %dt.keywords
    if dt.mimeTypes.len > 0:
      dtObj["mime_types"] = %dt.mimeTypes
      
    jsonObj["desktop"] = dtObj

  return $jsonObj

proc serializeManifestToKDL*(manifest: PackageManifest): string =
  ## Serialize manifest to KDL format (human-friendly)
  ## Complete implementation with all fields for perfect roundtrip
  result = "package \"" & manifest.name & "\" {\n"

  # Required fields
  result.add("  version \"" & $manifest.version & "\"\n")
  result.add("  license \"" & manifest.license & "\"\n")

  # Optional identity fields
  if manifest.description.isSome:
    result.add("  description \"" & manifest.description.get() & "\"\n")

  if manifest.homepage.isSome:
    result.add("  homepage \"" & manifest.homepage.get() & "\"\n")

  if manifest.author.isSome:
    result.add("  author \"" & manifest.author.get() & "\"\n")

  if manifest.timestamp.isSome:
    result.add("  timestamp \"" & manifest.timestamp.get() & "\"\n")

  # Runtime configuration
  if manifest.libc.isSome:
    result.add("  libc \"" & manifest.libc.get() & "\"\n")

  if manifest.allocator.isSome:
    result.add("  allocator \"" & manifest.allocator.get() & "\"\n")

  if manifest.buildSystem.isSome:
    result.add("  build_system \"" & manifest.buildSystem.get() & "\"\n")

  # Integrity hashes
  if manifest.buildHash.len > 0:
    result.add("  build_hash \"" & manifest.buildHash & "\"\n")

  if manifest.sourceHash.len > 0:
    result.add("  source_hash \"" & manifest.sourceHash & "\"\n")

  if manifest.artifactHash.len > 0:
    result.add("  artifact_hash \"" & manifest.artifactHash & "\"\n")

  # UTCP support
  if manifest.utcpEndpoint.isSome:
    result.add("  utcp_endpoint \"" & manifest.utcpEndpoint.get() & "\"\n")

  if manifest.utcpVersion.isSome:
    result.add("  utcp_version \"" & manifest.utcpVersion.get() & "\"\n")

  # Dependencies (child nodes)
  if manifest.dependencies.len > 0:
    result.add("\n  dependencies {\n")
    for dep in manifest.dependencies:
      result.add("    \"" & dep.name & "\"")
      if dep.versionConstraint.operator != OpAny:
        result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
      if dep.optional:
        result.add(" optional=true")
      if dep.features.len > 0:
        result.add(" features=\"" & dep.features.join(",") & "\"")
      result.add("\n")
    result.add("  }\n")

  # Build dependencies
  if manifest.buildDependencies.len > 0:
    result.add("\n  build_dependencies {\n")
    for dep in manifest.buildDependencies:
      result.add("    \"" & dep.name & "\"")
      if dep.versionConstraint.operator != OpAny:
        result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
      result.add("\n")
    result.add("  }\n")

  # Optional dependencies
  if manifest.optionalDependencies.len > 0:
    result.add("\n  optional_dependencies {\n")
    for dep in manifest.optionalDependencies:
      result.add("    \"" & dep.name & "\"")
      if dep.versionConstraint.operator != OpAny:
        result.add(" version=\"" & $dep.versionConstraint.operator & $dep.versionConstraint.version & "\"")
      if dep.features.len > 0:
        result.add(" features=\"" & dep.features.join(",") & "\"")
      result.add("\n")
    result.add("  }\n")

  # Build configuration
  if manifest.buildFlags.len > 0:
    result.add("\n  build_flags {\n")
    for flag in manifest.buildFlags:
      result.add("    \"" & flag & "\"\n")
    result.add("  }\n")

  if manifest.configureFlags.len > 0:
    result.add("\n  configure_flags {\n")
    for flag in manifest.configureFlags:
      result.add("    \"" & flag & "\"\n")
    result.add("  }\n")

  # Platform constraints
  if manifest.supportedOS.len > 0:
    result.add("\n  supported_os {\n")
    for os in manifest.supportedOS:
      result.add("    \"" & os & "\"\n")
    result.add("  }\n")

  if manifest.supportedArchitectures.len > 0:
    result.add("\n  supported_architectures {\n")
    for arch in manifest.supportedArchitectures:
      result.add("    \"" & arch & "\"\n")
    result.add("  }\n")

  if manifest.requiredCapabilities.len > 0:
    result.add("\n  required_capabilities {\n")
    for cap in manifest.requiredCapabilities:
      result.add("    \"" & cap & "\"\n")
    result.add("  }\n")

  # Metadata
  if manifest.tags.len > 0:
    result.add("\n  tags {\n")
    for tag in manifest.tags:
      result.add("    \"" & tag & "\"\n")
    result.add("  }\n")

  if manifest.maintainers.len > 0:
    result.add("\n  maintainers {\n")
    for maintainer in manifest.maintainers:
      result.add("    \"" & maintainer & "\"\n")
    result.add("  }\n")

  # System Integration
  if manifest.files.len > 0:
    result.add("\n  files {\n")
    for file in manifest.files:
      result.add("    file \"" & file.path & "\" hash=\"" & file.hash & "\" size=" & $file.size & " permissions=\"" & file.permissions & "\"\n")
    result.add("  }\n")

  if manifest.users.len > 0:
    result.add("\n  users {\n")
    for user in manifest.users:
      result.add("    \"" & user.name & "\" group=\"" & user.group & "\" shell=\"" & user.shell & "\" home=\"" & user.home & "\"")
      if user.uid.isSome:
        result.add(" uid=" & $user.uid.get())
      result.add("\n")
    result.add("  }\n")

  if manifest.groups.len > 0:
    result.add("\n  groups {\n")
    for group in manifest.groups:
      result.add("    \"" & group.name & "\"")
      if group.gid.isSome:
        result.add(" gid=" & $group.gid.get())
      result.add("\n")
    result.add("  }\n")

  if manifest.services.len > 0:
    result.add("\n  services {\n")
    for service in manifest.services:
      result.add("    \"" & service.name & "\" enabled=" & $service.enabled & " content=" & service.content.escape() & "\n")
    result.add("  }\n")

  # Security / Sandbox
  if manifest.sandbox.isSome:
    let sb = manifest.sandbox.get()
    result.add("\n  sandbox level=\"" & $sb.level & "\" {\n")
    
    # Linux
    if sb.seccompProfile.isSome or sb.capabilities.len > 0 or sb.namespaces.len > 0:
      result.add("    linux")
      if sb.seccompProfile.isSome:
        result.add(" seccomp=\"" & sb.seccompProfile.get() & "\"")
      result.add(" {\n")
      
      if sb.capabilities.len > 0:
        result.add("      capabilities")
        for cap in sb.capabilities:
          result.add(" \"" & cap & "\"")
        result.add("\n")
        
      if sb.namespaces.len > 0:
        result.add("      namespaces")
        for ns in sb.namespaces:
          result.add(" \"" & ns & "\"")
        result.add("\n")
      result.add("    }\n")
      
    # BSD
    if sb.pledge.isSome or sb.unveil.len > 0:
      result.add("    bsd")
      if sb.pledge.isSome:
        result.add(" pledge=\"" & sb.pledge.get() & "\"")
      result.add(" {\n")
      
      if sb.unveil.len > 0:
        result.add("      unveil")
        for path in sb.unveil:
          result.add(" \"" & path & "\"")
        result.add("\n")
      result.add("    }\n")
      
    result.add("  }\n")

  # Desktop Integration
  if manifest.desktop.isSome:
    let dt = manifest.desktop.get()
    result.add("\n  desktop display_name=\"" & dt.displayName & "\" terminal=" & $dt.terminal & " startup_notify=" & $dt.startupNotify)
    if dt.icon.isSome:
      result.add(" icon=\"" & dt.icon.get() & "\"")
    if dt.startupWMClass.isSome:
      result.add(" startup_wm_class=\"" & dt.startupWMClass.get() & "\"")
    result.add(" {\n")
    
    if dt.categories.len > 0:
      result.add("    categories")
      for cat in dt.categories:
        result.add(" \"" & cat & "\"")
      result.add("\n")
      
    if dt.keywords.len > 0:
      result.add("    keywords")
      for kw in dt.keywords:
        result.add(" \"" & kw & "\"")
      result.add("\n")
      
    if dt.mimeTypes.len > 0:
      result.add("    mime_types")
      for mt in dt.mimeTypes:
        result.add(" \"" & mt & "\"")
      result.add("\n")
      
    result.add("  }\n")

  result.add("}\n")

# ============================================================================
# Manifest Hash Calculation
# ============================================================================

proc calculateManifestHash*(manifest: PackageManifest): string =
  ## Calculate deterministic xxh3-128 hash of manifest
  ##
  ## **Purpose:** Provides a unique identifier for a specific manifest configuration
  ## **Algorithm:** xxh3-128 (fast, 128-bit collision resistance)
  ## **Determinism:** Same manifest always produces same hash
  ##
  ## **Hash Components (in order):**
  ## 1. Format type
  ## 2. Package identity (name, version)
  ## 3. Dependencies (sorted by name for determinism)
  ## 4. Build configuration (sorted flags)
  ## 5. Platform constraints (sorted)
  ## 6. Integrity hashes (build, source, artifact)
  ## 7. Metadata (sorted)
  ##
  ## **Requirements:** 6.5, 7.5
  ## **Property:** Manifest Hash Determinism (Property 9)

  var components: seq[string] = @[]

  # 1. Format type (ensures different formats have different hashes)
  components.add($manifest.format)

  # 2. Package identity
  components.add(manifest.name)
  components.add($manifest.version)
  components.add(manifest.license)

  # 3. Optional identity fields (sorted for determinism)
  if manifest.description.isSome:
    components.add("description:" & manifest.description.get())
  if manifest.homepage.isSome:
    components.add("homepage:" & manifest.homepage.get())
  if manifest.author.isSome:
    components.add("author:" & manifest.author.get())

  # 4. Dependencies (sorted by name for determinism)
  var depStrings: seq[string] = @[]
  for dep in manifest.dependencies:
    var depStr = "dep:" & dep.name
    if dep.versionConstraint.operator != OpAny:
      depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
    if dep.optional:
      depStr.add(":optional")
    if dep.features.len > 0:
      depStr.add(":features=" & dep.features.sorted().join(","))
    depStrings.add(depStr)
  components.add(depStrings.sorted().join("|"))

  # 5. Build dependencies (sorted)
  var buildDepStrings: seq[string] = @[]
  for dep in manifest.buildDependencies:
    var depStr = "builddep:" & dep.name
    if dep.versionConstraint.operator != OpAny:
      depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
    buildDepStrings.add(depStr)
  components.add(buildDepStrings.sorted().join("|"))

  # 6. Optional dependencies (sorted)
  var optDepStrings: seq[string] = @[]
  for dep in manifest.optionalDependencies:
    var depStr = "optdep:" & dep.name
    if dep.versionConstraint.operator != OpAny:
      depStr.add(":" & $dep.versionConstraint.operator & $dep.versionConstraint.version)
    if dep.features.len > 0:
      depStr.add(":features=" & dep.features.sorted().join(","))
    optDepStrings.add(depStr)
  components.add(optDepStrings.sorted().join("|"))

  # 7. Build configuration (sorted flags for determinism)
  if manifest.buildSystem.isSome:
    components.add("buildsystem:" & manifest.buildSystem.get())
  components.add("buildflags:" & manifest.buildFlags.sorted().join(" "))
  components.add("configureflags:" & manifest.configureFlags.sorted().join(" "))

  # 8. Runtime configuration
  if manifest.libc.isSome:
    components.add("libc:" & manifest.libc.get())
  if manifest.allocator.isSome:
    components.add("allocator:" & manifest.allocator.get())

  # 9. Platform constraints (sorted for determinism)
  components.add("os:" & manifest.supportedOS.sorted().join(","))
  components.add("arch:" & manifest.supportedArchitectures.sorted().join(","))
  components.add("caps:" & manifest.requiredCapabilities.sorted().join(","))

  # 10. Integrity hashes (these are already deterministic)
  components.add("buildhash:" & manifest.buildHash)
  components.add("sourcehash:" & manifest.sourceHash)
  components.add("artifacthash:" & manifest.artifactHash)

  # 11. Metadata (sorted for determinism)
  if manifest.timestamp.isSome:
    components.add("timestamp:" & manifest.timestamp.get())
  components.add("tags:" & manifest.tags.sorted().join(","))
  components.add("maintainers:" & manifest.maintainers.sorted().join(","))

  # 12. UTCP support
  if manifest.utcpEndpoint.isSome:
    components.add("utcp:" & manifest.utcpEndpoint.get())
  if manifest.utcpVersion.isSome:
    components.add("utcpver:" & manifest.utcpVersion.get())

  # 13. System Integration (sorted for determinism)
  var fileStrings: seq[string] = @[]
  for file in manifest.files:
    fileStrings.add("file:" & file.path & ":" & file.hash & ":" & $file.size & ":" & file.permissions)
  components.add(fileStrings.sorted().join("|"))

  var userStrings: seq[string] = @[]
  for user in manifest.users:
    var s = "user:" & user.name & ":" & user.group & ":" & user.shell & ":" & user.home
    if user.uid.isSome: s.add(":" & $user.uid.get())
    userStrings.add(s)
  components.add(userStrings.sorted().join("|"))

  var groupStrings: seq[string] = @[]
  for group in manifest.groups:
    var s = "group:" & group.name
    if group.gid.isSome: s.add(":" & $group.gid.get())
    groupStrings.add(s)
  components.add(groupStrings.sorted().join("|"))

  var serviceStrings: seq[string] = @[]
  for service in manifest.services:
    serviceStrings.add("service:" & service.name & ":" & $service.enabled & ":" & service.content)
  components.add(serviceStrings.sorted().join("|"))

  # 14. Security / Sandbox
  if manifest.sandbox.isSome:
    let sb = manifest.sandbox.get()
    var sbStr = "sandbox:" & $sb.level
    
    if sb.seccompProfile.isSome:
      sbStr.add(":seccomp=" & sb.seccompProfile.get())
    if sb.capabilities.len > 0:
      sbStr.add(":caps=" & sb.capabilities.sorted().join(","))
    if sb.namespaces.len > 0:
      sbStr.add(":ns=" & sb.namespaces.sorted().join(","))
      
    if sb.pledge.isSome:
      sbStr.add(":pledge=" & sb.pledge.get())
    if sb.unveil.len > 0:
      sbStr.add(":unveil=" & sb.unveil.sorted().join(","))
      
    components.add(sbStr)

  # 15. Desktop Integration
  if manifest.desktop.isSome:
    let dt = manifest.desktop.get()
    var dtStr = "desktop:" & dt.displayName & ":" & $dt.terminal & ":" & $dt.startupNotify
    
    if dt.icon.isSome:
      dtStr.add(":icon=" & dt.icon.get())
    if dt.startupWMClass.isSome:
      dtStr.add(":wmclass=" & dt.startupWMClass.get())
    if dt.categories.len > 0:
      dtStr.add(":cats=" & dt.categories.sorted().join(","))
    if dt.keywords.len > 0:
      dtStr.add(":kws=" & dt.keywords.sorted().join(","))
    if dt.mimeTypes.len > 0:
      dtStr.add(":mimes=" & dt.mimeTypes.sorted().join(","))
      
    components.add(dtStr)

  # Calculate hash from all components
  let input = components.join("|")
  let hash = calculateXXH3(input)

  return $hash

proc verifyManifestHash*(manifest: PackageManifest, expectedHash: string): bool =
  ## Verify that a manifest matches the expected hash
  ## Returns true if hash matches, false otherwise
  let calculatedHash = calculateManifestHash(manifest)
  return calculatedHash == expectedHash

# ============================================================================
# Convenience
# ============================================================================

proc `$`*(manifest: PackageManifest): string =
  ## Convert manifest to human-readable string
  result = "Package: " & manifest.name & " v" & $manifest.version & "\n"
  result.add("License: " & manifest.license & "\n")
  result.add("Format: " & $manifest.format & "\n")

  if manifest.description.isSome:
    result.add("Description: " & manifest.description.get() & "\n")

  if manifest.dependencies.len > 0:
    result.add("Dependencies: " & $manifest.dependencies.len & "\n")

  if manifest.supportedOS.len > 0:
    result.add("OS: " & manifest.supportedOS.join(", ") & "\n")

  if manifest.supportedArchitectures.len > 0:
    result.add("Architectures: " & manifest.supportedArchitectures.join(", ") & "\n")

when isMainModule:
  echo "Manifest Parser - Systems Engineering Approach"
  echo "Format-agnostic, strict validation, platform-aware"
  echo ""

  # Example JSON manifest
  let jsonExample = """
{
  "name": "example-package",
  "version": "1.2.3",
  "license": "MIT",
  "description": "An example package",
  "supported_os": ["linux", "freebsd"],
  "supported_architectures": ["x86_64", "aarch64"],
  "dependencies": [
    {"name": "dep1", "version": ">=1.0.0"},
    {"name": "dep2", "version": "~2.0.0", "optional": true}
  ],
  "build_hash": "blake3-abc123",
  "utcp_endpoint": "https://packages.nexusos.org/utcp/v1/manifest/example-package"
}
"""

  echo "Parsing JSON manifest..."
  let manifest = parseManifest(jsonExample, NPK, FormatJSON, ValidationStrict)
  echo manifest
  echo ""

  echo "Serializing to KDL..."
  echo serializeManifestToKDL(manifest)
