## graft_commands.nim
## CLI commands for MVP grafting functionality

import std/[strformat, strutils, tables, times, json, os, options, posix]
import ../graft_coordinator, ../install_manager, ../system_integration, ../config, ../logger, ../platform
import ../variant_manager, ../variant_profiles, ../variant_types, ../variant_parser, ../variant_paths, ../variant_database
import ../variant_domains, ../variant_compiler, ../variant_migration

# Global coordinator instance
var globalCoordinator: GraftCoordinator = nil

proc initGraftCommands*(verbose: bool = false) =
  ## Initialize the graft command system

  # Initialize logging
  let logLevel = if verbose: Debug else: Info
  # Use user-appropriate log path
  let logPath = if getuid() == 0: "/var/log/nip.log" else: getEnv("XDG_CACHE_HOME", getHomeDir() / ".cache") / "nip" / "nip.log"
  initGlobalLogger(logPath, logLevel, verbose)
  logInfo("NIP MVP started")

  # Load proper config (XDG-compliant, user-aware)
  let nipConfig = loadConfig()

  # Convert NipConfig to InstallConfig for coordinator
  let installConfig = InstallConfig(
    programsDir: nipConfig.programsDir,
    linksDir: nipConfig.linksDir,
    cacheDir: nipConfig.cacheDir,
    dbFile: nipConfig.dbFile,
    autoSymlink: nipConfig.autoSymlink,
    checkConflicts: nipConfig.checkConflicts,
    verbose: verbose
  )

  globalCoordinator = newGraftCoordinator(installConfig, verbose)

proc ensureInitialized() =
  ## Ensure coordinator is initialized
  if globalCoordinator == nil:
    initGraftCommands()

proc graftCommand*(packageSpec: string, verbose: bool = false): int =
  ## Graft a package from specified source
  ## Usage: nip graft [source:]package
  ## Examples:
  ##   nip graft nix:firefox
  ##   nip graft pkgsrc:vim
  ##   nip graft hello  (auto-detect source)

  ensureInitialized()
  if verbose:
    globalCoordinator.verbose = true

  echo fmt"🌱 NIP Graft - Universal Package Grafting"
  echo ""

  # Parse package spec
  let (source, packageName) = parsePackageSpec(packageSpec)

  logOperation("graft", fmt"package={packageName} source={source}")

  if source != Auto:
    echo fmt"📦 Package: {packageName}"
    echo fmt"� Soukrce: {source}"
  else:
    echo fmt"📦 Package: {packageName}"
    echo fmt"🔍 Source: Auto-detect"
  echo ""

  # Check if already installed
  if globalCoordinator.isInstalled(packageName):
    echo fmt"⚠️  Package '{packageName}' is already installed"
    echo fmt"   Use 'nip remove {packageName}' first to reinstall"
    logWarning(fmt"Attempted to install already installed package: {packageName}")
    return 1

  # Perform graft
  let result = globalCoordinator.graft(packageName, source)

  if result.success:
    echo ""
    echo "✅ Graft successful!"
    echo fmt"📍 Installed to: {result.installPath}"
    echo fmt"🔗 Symlinks created in /System/Links/"
    echo ""
    echo fmt"You can now run: {packageName}"
    logSuccess("graft", fmt"{packageName} installed to {result.installPath}")
    return 0
  else:
    echo ""
    echo "❌ Graft failed!"
    for error in result.errors:
      echo fmt"   Error: {error}"
      logError(fmt"Graft error: {error}")
    for warning in result.warnings:
      echo fmt"   Warning: {warning}"
      logWarning(fmt"Graft warning: {warning}")
    logFailure("graft", fmt"Failed to install {packageName}")
    return 1

proc removeCommand*(packageName: string, verbose: bool = false): int =
  ## Remove an installed package
  ensureInitialized()
  if verbose:
    globalCoordinator.verbose = true

  logOperation("remove", packageName)

  echo fmt"🗑️  Removing package: {packageName}"
  echo ""

  if not globalCoordinator.isInstalled(packageName):
    echo fmt"❌ Package '{packageName}' is not installed"
    logWarning(fmt"Attempted to remove non-installed package: {packageName}")
    return 1

  let result = globalCoordinator.remove(packageName)

  if result.success:
    echo "✅ Package removed successfully"
    logSuccess("remove", packageName)
    return 0
  else:
    echo "❌ Removal failed!"
    for error in result.errors:
      echo fmt"   Error: {error}"
      logError(fmt"Remove error: {error}")
    logFailure("remove", packageName)
    return 1

proc listCommand*(source: string = "", verbose: bool = false): int =
  ## List installed packages
  ensureInitialized()

  let packages = globalCoordinator.list(source)

  if packages.len == 0:
    if source != "":
      echo fmt"No packages installed from source: {source}"
    else:
      echo "No packages installed"
    return 0

  echo "📦 Installed Packages"
  echo ""
  echo "Name                 Version          Source      Installed           "
  echo "─".repeat(70)

  for pkg in packages:
    let installedDate = pkg.installedAt.format("yyyy-MM-dd HH:mm")
    echo fmt"{pkg.name:<20} {pkg.version:<15} {pkg.source:<10} {installedDate:<20}"

  echo ""
  echo fmt"Total: {packages.len} packages"

  return 0

proc infoCommand*(packageName: string, verbose: bool = false): int =
  ## Show detailed information about an installed package
  ensureInitialized()

  if not globalCoordinator.isInstalled(packageName):
    echo fmt"❌ Package '{packageName}' is not installed"
    return 1

  let pkg = globalCoordinator.info(packageName)

  echo "📦 Package Information"
  echo ""
  let installedStr = pkg.installedAt.format("yyyy-MM-dd HH:mm:ss")
  echo fmt"Name:         {pkg.name}"
  echo fmt"Version:      {pkg.version}"
  echo fmt"Source:       {pkg.source}"
  echo fmt"Installed:    {installedStr}"
  echo fmt"Install Path: {pkg.installPath}"
  echo fmt"Graft Hash:   {pkg.graftHash}"
  echo ""

  if pkg.dependencies.len > 0:
    echo "Dependencies:"
    for dep in pkg.dependencies:
      echo fmt"  - {dep}"
    echo ""

  if pkg.symlinks.len > 0:
    echo fmt"Symlinks: {pkg.symlinks.len} created"
    if verbose:
      for symlink in pkg.symlinks:
        echo fmt"  - {symlink}"
      echo ""

  if pkg.files.len > 0:
    echo fmt"Files: {pkg.files.len} installed"
    if verbose:
      echo "First 10 files:"
      for i, file in pkg.files:
        if i >= 10:
          echo fmt"  ... and {pkg.files.len - 10} more"
          break
        echo fmt"  - {file}"

  return 0

proc statusCommand*(verbose: bool = false): int =
  ## Show system status
  ensureInitialized()

  let status = globalCoordinator.status()

  echo "🔍 NIP System Status"
  echo ""
  if status.hasKey("total_packages"):
    let totalPkgs = status["total_packages"].getInt()
    echo fmt"Total Packages:  {totalPkgs}"
  echo ""

  echo "By Source:"
  let bySource = if status.hasKey("by_source"): status["by_source"] else: newJObject()
  for source, count in bySource:
    echo fmt"  {source:<10} {count.getInt()} packages"

  echo ""
  if status.hasKey("total_size_mb"):
    let totalSize = status["total_size_mb"].getInt()
    echo fmt"Total Size:      {totalSize} MB"
  echo ""
  echo "Directories:"
  if status.hasKey("programs_dir"):
    let programsDir = status["programs_dir"].getStr()
    echo fmt"  Programs:      {programsDir}"
  if status.hasKey("links_dir"):
    let linksDir = status["links_dir"].getStr()
    echo fmt"  Links:         {linksDir}"
  if status.hasKey("database"):
    let dbPath = status["database"].getStr()
    echo fmt"  Database:      {dbPath}"

  return 0

proc searchCommand*(query: string, source: string = "", verbose: bool = false): int =
  ## Search for packages (placeholder for MVP)
  echo fmt"🔍 Searching for: {query}"

  if source != "":
    echo fmt"   Source: {source}"

  echo ""
  echo "⚠️  Search functionality coming in v0.2"
  echo "   For now, you can:"
  echo "   - Use 'nix search <query>' to search Nix packages"
  echo "   - Use 'pkgsrc search <query>' to search PKGSRC"
  echo "   - Then graft with: nip graft <source>:<package>"

  return 0

proc doctorCommand*(verbose: bool = false): int =
  ## Check system health
  ensureInitialized()

  echo "🏥 NIP System Health Check"
  echo ""

  var issues = 0
  var warnings = 0

  # Check directories
  let config = globalCoordinator.installManager.config

  echo "Checking directories..."
  if dirExists(config.programsDir):
    echo fmt"  ✅ Programs directory: {config.programsDir}"
  else:
    echo fmt"  ❌ Programs directory missing: {config.programsDir}"
    issues.inc

  if dirExists(config.linksDir):
    echo fmt"  ✅ Links directory: {config.linksDir}"
  else:
    echo fmt"  ❌ Links directory missing: {config.linksDir}"
    issues.inc

  if fileExists(config.dbFile):
    echo fmt"  ✅ Database file: {config.dbFile}"
  else:
    echo fmt"  ⚠️  Database file not found (will be created)"
    warnings.inc

  echo ""

  # Check installed packages
  let packages = globalCoordinator.list()
  echo fmt"Checking {packages.len} installed packages..."

  for pkg in packages:
    if not dirExists(pkg.installPath):
      echo fmt"  ❌ Missing: {pkg.name} at {pkg.installPath}"
      issues.inc
    else:
      # Check symlinks
      var brokenSymlinks = 0
      for symlink in pkg.symlinks:
        if not symlinkExists(symlink) and not fileExists(symlink):
          brokenSymlinks.inc

      if brokenSymlinks > 0:
        echo fmt"  ⚠️  {pkg.name}: {brokenSymlinks} broken symlinks"
        warnings.inc

  echo ""
  echo "Summary:"
  if issues == 0 and warnings == 0:
    echo "  ✅ All checks passed!"
    return 0
  else:
    if issues > 0:
      echo fmt"  ❌ {issues} issues found"
    if warnings > 0:
      echo fmt"  ⚠️  {warnings} warnings"
    return if issues > 0: 1 else: 0


proc setupCommand*(verbose: bool = false): int =
  ## Setup system integration (PATH, libraries)
  echo "🔧 NIP System Integration Setup"
  echo ""
  echo "This will configure your system to use NIP-managed packages."
  echo "Requires root/sudo permissions."
  echo ""

  let linksDir = "/System/Links"

  if setupSystemIntegration(linksDir):
    echo ""
    echo "🎉 Setup complete!"
    echo ""
    showShellIntegrationHelp()
    return 0
  else:
    echo ""
    echo "⚠️  Setup incomplete. You may need to run with sudo:"
    echo "   sudo ./nip_mvp setup"
    echo ""
    showShellIntegrationHelp()
    return 1


proc configCommand*(action: string = "show", verbose: bool = false): int =
  ## Manage NIP configuration
  case action
  of "show":
    echo "📋 NIP Configuration"
    echo ""

    let cfg = loadConfig()
    echo fmt"Programs Directory:  {cfg.programsDir}"
    echo fmt"Links Directory:     {cfg.linksDir}"
    echo fmt"Cache Directory:     {cfg.cacheDir}"
    echo fmt"Database File:       {cfg.dbFile}"
    echo ""
    echo fmt"Auto Symlink:        {cfg.autoSymlink}"
    echo fmt"Check Conflicts:     {cfg.checkConflicts}"
    echo fmt"Verbose:             {cfg.verbose}"
    echo ""
    echo "Adapters:"
    for name, adapterCfg in cfg.adapters:
      let status = if adapterCfg.enabled: "enabled" else: "disabled"
      echo fmt"  {name:<10} {status:<10} priority: {adapterCfg.priority}"

    return 0

  of "init":
    echo "🔧 Initializing NIP configuration"
    echo ""

    let userConfigPath = getHomeDir() / ".nip" / "config"
    if fileExists(userConfigPath):
      echo fmt"⚠️  Config already exists at: {userConfigPath}"
      echo "   Use 'nip config edit' to modify it"
      return 1

    if saveExampleConfig(userConfigPath):
      echo ""
      echo "You can now edit the config file to customize NIP."
      return 0
    else:
      return 1

  of "path":
    let cfg = loadConfig()
    echo cfg.programsDir
    return 0

  else:
    echo fmt"Unknown config action: {action}"
    echo "Usage: nip config [show|init|path]"
    return 1


proc logsCommand*(lines: int = 50, verbose: bool = false): int =
  ## Show recent log entries
  let logPath = getLogPath()

  if not fileExists(logPath):
    echo fmt"📋 No log file found at: {logPath}"
    echo ""
    echo "Logs will be created when NIP performs operations."
    return 0

  echo fmt"📋 NIP Logs (last {lines} lines)"
  echo fmt"   Log file: {logPath}"
  echo ""

  try:
    let content = readFile(logPath)
    let allLines = content.splitLines()
    let startIdx = max(0, allLines.len - lines)

    for i in startIdx..<allLines.len:
      if allLines[i].len > 0:
        echo allLines[i]

    return 0
  except:
    echo fmt"❌ Could not read log file: {logPath}"
    return 1


proc platformCommand*(verbose: bool = false): int =
  ## Show platform information
  showPlatformInfo()
  return 0


# #############################################################################
# Variant-Enhanced Graft Commands
# #############################################################################

# Global variant manager
var globalVariantManager: VariantManager = nil

proc initVariantManager*() =
  ## Initialize variant manager
  if globalVariantManager == nil:
    let dbPath = expandTilde("~/.nip")
    globalVariantManager = newVariantManager(dbPath)

proc graftWithVariants*(
  packageSpec: string,
  domainFlags: seq[string] = @[],
  profilePath: string = "",
  verbose: bool = false
): int =
  ## Enhanced graft command with variant support
  ## Usage: nip graft [source:]package [+domain=value...] [--profile=path]
  ## Examples:
  ##   nip graft firefox +init=dinit +graphics=wayland
  ##   nip graft vim --profile=gaming-rig.kdl
  ##   nip graft htop +security=pie,relro +optimization=lto

  ensureInitialized()
  initVariantManager()

  if verbose:
    globalCoordinator.verbose = true

  echo "🌱 NIP Graft - Universal Package Grafting with Variants"
  echo ""

  # Parse package spec
  let (source, packageName) = parsePackageSpec(packageSpec)

  logOperation("graft-variant", fmt"package={packageName} source={source} flags={domainFlags.len}")

  echo fmt"📦 Package: {packageName}"
  if source != Auto:
    echo fmt"🔍 Source: {source}"
  else:
    echo fmt"🔍 Source: Auto-detect"

  # Create variant if flags or profile specified
  var variantResult: VariantCreationResult
  var useVariant = false

  if domainFlags.len > 0 or profilePath.len > 0:
    useVariant = true
    echo ""
    echo "🎯 Creating variant configuration..."

    # Get base compiler flags from config
    let nipConfig = loadConfig()
    let baseCompilerFlags = nipConfig.compilerFlags

    if profilePath.len > 0:
      # Load profile
      echo fmt"📋 Loading profile: {profilePath}"
      try:
        let profile = loadProfile(profilePath)
        echo fmt"   Profile: {profile.name} - {profile.description}"

        # Parse domain flag overrides
        var overrides = initTable[string, seq[string]]()
        if domainFlags.len > 0:
          echo "   Applying overrides:"
          for flagStr in domainFlags:
            try:
              let flag = parseDomainFlag(flagStr)
              if overrides.hasKey(flag.domain):
                overrides[flag.domain].add(flag.name)
              else:
                overrides[flag.domain] = @[flag.name]
              echo fmt"     {flag.domain} = {flag.name}"
            except:
              echo fmt"❌ Invalid flag: {flagStr}"
              return 1

        # Create variant from profile
        variantResult = globalVariantManager.createVariantFromProfile(
          packageName,
          "latest",  # Version will be determined during graft
          profile,
          overrides
        )

      except:
        echo fmt"❌ Failed to load profile: {profilePath}"
        return 1

    else:
      # Create variant from flags only
      echo "   Domain flags:"
      for flag in domainFlags:
        echo fmt"     {flag}"

      variantResult = globalVariantManager.createVariantFromFlags(
        packageName,
        "latest",
        domainFlags,
        baseCompilerFlags
      )

    if not variantResult.success:
      echo ""
      echo fmt"❌ Variant creation failed: {variantResult.error}"
      return 1

    echo ""
    echo "✅ Variant created:"
    echo fmt"   Fingerprint: {variantResult.fingerprint.hash}"
    let variantPath = generateVariantPath(packageName, "latest", variantResult.fingerprint.hash)
    echo "   Install path: " & variantPath

  # Check if already installed
  if globalCoordinator.isInstalled(packageName):
    echo ""
    echo fmt"⚠️  Package '{packageName}' is already installed"
    echo fmt"   Use 'nip remove {packageName}' first to reinstall"
    return 1

  # Perform graft
  echo ""
  echo "🔧 Grafting package..."
  let result = globalCoordinator.graft(packageName, source)

  if result.success:
    echo ""
    echo "✅ Graft successful!"
    echo fmt"📍 Installed to: {result.installPath}"
    echo fmt"🔗 Symlinks created in /System/Links/"

    if useVariant:
      echo ""
      echo "📊 Variant Information:"
      echo fmt"   Fingerprint: {variantResult.fingerprint.hash}"
      echo "   Domains:"
      for domain, values in variantResult.fingerprint.domainFlags:
        echo "     " & domain & ": " & values.join(", ")

    echo ""
    echo fmt"You can now run: {packageName}"
    logSuccess("graft-variant", fmt"{packageName} installed with variant")
    return 0
  else:
    echo ""
    echo "❌ Graft failed!"
    for error in result.errors:
      echo fmt"   Error: {error}"
    return 1


# #############################################################################
# Variant Management Commands
# #############################################################################

proc variantListCommand*(packageName: string, jsonOutput: bool = false): int =
  ## List all variants of a package
  ## Usage: nip variant list <package> [--json]

  initVariantManager()

  if jsonOutput:
    # JSON output
    var jsonArray = newJArray()
    let variants = globalVariantManager.listVariants(packageName)

    for variant in variants:
      var variantJson = %*{
        "fingerprint": variant.fingerprint,
        "package": variant.packageName,
        "version": variant.version,
        "installPath": variant.installPath,
        "installedAt": variant.installedAt.toUnix(),
        "toolchain": {
          "name": variant.toolchain.name,
          "version": variant.toolchain.version
        },
        "target": {
          "arch": variant.target.arch,
          "os": variant.target.os
        },
        "domains": newJObject()
      }

      # Add domains
      for domain, values in variant.domains:
        variantJson["domains"][domain] = %values

      jsonArray.add(variantJson)

    echo jsonArray.pretty()
    return 0

  else:
    # Human-readable output
    let variants = globalVariantManager.listVariants(packageName)

    if variants.len == 0:
      echo fmt"No variants found for package: {packageName}"
      return 0

    echo fmt"📦 Variants of {packageName}:"
    echo ""

    for i, variant in variants:
      echo fmt"[{i + 1}] {variant.fingerprint}"
      echo fmt"    Version: {variant.version}"
      echo fmt"    Path: {variant.installPath}"
      let installedTime = variant.installedAt.format("yyyy-MM-dd HH:mm:ss")
      echo "    Installed: " & installedTime
      echo fmt"    Toolchain: {variant.toolchain.name}-{variant.toolchain.version}"
      echo fmt"    Target: {variant.target.arch}-{variant.target.os}"

      if variant.domains.len > 0:
        echo "    Domains:"
        for domain, values in variant.domains:
          echo "      " & domain & ": " & values.join(", ")

      if i < variants.len - 1:
        echo ""

    echo ""
    echo fmt"Total: {variants.len} variant(s)"
    return 0

proc variantInfoCommand*(fingerprint: string, jsonOutput: bool = false): int =
  ## Show detailed information about a variant
  ## Usage: nip variant info <fingerprint> [--json]

  initVariantManager()

  let info = globalVariantManager.getVariantInfo(fingerprint)

  if not info.isSome():
    echo fmt"❌ Variant not found: {fingerprint}"
    return 1

  let variant = info.get()

  if jsonOutput:
    var variantJson = %*{
      "fingerprint": variant.fingerprint,
      "package": variant.packageName,
      "version": variant.version,
      "installPath": variant.installPath,
      "installedAt": variant.installedAt.toUnix(),
      "toolchain": {
        "name": variant.toolchain.name,
        "version": variant.toolchain.version
      },
      "target": {
        "arch": variant.target.arch,
        "os": variant.target.os
      },
      "domains": newJObject()
    }

    for domain, values in variant.domains:
      variantJson["domains"][domain] = %values

    echo variantJson.pretty()
  else:
    echo prettyPrint(variant)

  return 0

proc variantIdCommand*(
  packageName: string,
  version: string,
  domainFlags: seq[string]
): int =
  ## Calculate variant fingerprint without creating it
  ## Usage: nip variant id <package> <version> +domain=value...

  initVariantManager()

  echo fmt"🔍 Calculating variant ID for {packageName} {version}"
  echo ""

  # Get base compiler flags
  let nipConfig = loadConfig()
  let baseCompilerFlags = nipConfig.compilerFlags

  # Parse domain flags
  var domains = initTable[string, seq[string]]()
  echo "Domain flags:"
  for flagStr in domainFlags:
    try:
      let flag = parseDomainFlag(flagStr)
      if domains.hasKey(flag.domain):
        domains[flag.domain].add(flag.name)
      else:
        domains[flag.domain] = @[flag.name]
      echo fmt"  {flag.domain} = {flag.name}"
    except:
      echo fmt"❌ Invalid flag: {flagStr}"
      return 1

  # Calculate fingerprint
  let fingerprint = globalVariantManager.calculateVariantId(
    packageName,
    version,
    domains,
    baseCompilerFlags
  )

  echo ""
  echo "✅ Variant ID:"
  echo fmt"   {fingerprint}"
  echo ""
  echo "This variant:"
  let exists = globalVariantManager.hasVariant(fingerprint)
  if exists:
    echo "   ✓ Already exists in database"
  else:
    echo "   ✗ Does not exist yet"

  return 0

proc variantDeleteCommand*(fingerprint: string): int =
  ## Delete a variant from the database
  ## Usage: nip variant delete <fingerprint>

  initVariantManager()

  echo fmt"🗑️  Deleting variant: {fingerprint}"

  let deleted = globalVariantManager.deleteVariant(fingerprint)

  if deleted:
    echo "✅ Variant deleted successfully"
    return 0
  else:
    echo "❌ Variant not found"
    return 1

proc variantCountCommand*(packageName: string): int =
  ## Count variants for a package
  ## Usage: nip variant count <package>

  initVariantManager()

  let count = globalVariantManager.countVariants(packageName)

  echo fmt"📊 {packageName}: {count} variant(s)"
  return 0

proc variantDiffCommand*(fingerprint1: string, fingerprint2: string, jsonOutput: bool = false): int =
  ## Compare two variants and show differences
  ## Usage: nip variant diff <fingerprint1> <fingerprint2> [--json]

  initVariantManager()

  echo fmt"🔍 Comparing variants:"
  echo fmt"   {fingerprint1}"
  echo fmt"   {fingerprint2}"
  echo ""

  # Query both variants
  let variant1 = globalVariantManager.getVariantInfo(fingerprint1)
  let variant2 = globalVariantManager.getVariantInfo(fingerprint2)

  if not variant1.isSome():
    echo fmt"❌ Variant not found: {fingerprint1}"
    return 1

  if not variant2.isSome():
    echo fmt"❌ Variant not found: {fingerprint2}"
    return 1

  let v1 = variant1.get()
  let v2 = variant2.get()

  # Check if same package
  if v1.packageName != v2.packageName:
    echo fmt"⚠️  Warning: Comparing different packages ({v1.packageName} vs {v2.packageName})"
    echo ""

  if jsonOutput:
    # JSON output
    var diffJson = %*{
      "variant1": {
        "fingerprint": v1.fingerprint,
        "package": v1.packageName,
        "version": v1.version
      },
      "variant2": {
        "fingerprint": v2.fingerprint,
        "package": v2.packageName,
        "version": v2.version
      },
      "differences": {
        "added": newJObject(),
        "removed": newJObject(),
        "changed": newJObject()
      }
    }

    # Find domain differences
    var allDomains: seq[string] = @[]
    for domain in v1.domains.keys:
      if domain notin allDomains:
        allDomains.add(domain)
    for domain in v2.domains.keys:
      if domain notin allDomains:
        allDomains.add(domain)

    for domain in allDomains:
      let inV1 = v1.domains.hasKey(domain)
      let inV2 = v2.domains.hasKey(domain)

      if inV1 and not inV2:
        # Domain removed
        diffJson["differences"]["removed"][domain] = %v1.domains[domain]
      elif not inV1 and inV2:
        # Domain added
        diffJson["differences"]["added"][domain] = %v2.domains[domain]
      elif inV1 and inV2:
        # Check if values changed
        let values1 = v1.domains[domain]
        let values2 = v2.domains[domain]
        if values1 != values2:
          diffJson["differences"]["changed"][domain] = %*{
            "from": values1,
            "to": values2
          }

    echo diffJson.pretty()
    return 0

  else:
    # Human-readable output
    echo "Package Information:"
    echo fmt"   Variant 1: {v1.packageName} {v1.version}"
    echo fmt"   Variant 2: {v2.packageName} {v2.version}"
    echo ""

    # Find all unique domains
    var allDomains: seq[string] = @[]
    for domain in v1.domains.keys:
      if domain notin allDomains:
        allDomains.add(domain)
    for domain in v2.domains.keys:
      if domain notin allDomains:
        allDomains.add(domain)

    # Categorize differences
    type
      DomainChange = tuple[domain: string, fromVals: seq[string], toVals: seq[string]]
      DomainItem = tuple[domain: string, values: seq[string]]

    var added: seq[DomainItem] = @[]
    var removed: seq[DomainItem] = @[]
    var changed: seq[DomainChange] = @[]
    var unchanged: seq[DomainItem] = @[]

    for domain in allDomains:
      let inV1 = v1.domains.hasKey(domain)
      let inV2 = v2.domains.hasKey(domain)

      if inV1 and not inV2:
        removed.add((domain: domain, values: v1.domains[domain]))
      elif not inV1 and inV2:
        added.add((domain: domain, values: v2.domains[domain]))
      elif inV1 and inV2:
        let values1 = v1.domains[domain]
        let values2 = v2.domains[domain]
        if values1 != values2:
          changed.add((domain: domain, fromVals: values1, toVals: values2))
        else:
          unchanged.add((domain: domain, values: values1))

    # Display differences
    if added.len > 0:
      echo "➕ Added Domains:"
      for item in added:
        let valuesStr = item.values.join(", ")
        echo fmt"   {item.domain}: {valuesStr}"
      echo ""

    if removed.len > 0:
      echo "➖ Removed Domains:"
      for item in removed:
        let valuesStr = item.values.join(", ")
        echo fmt"   {item.domain}: {valuesStr}"
      echo ""

    if changed.len > 0:
      echo "🔄 Changed Domains:"
      for item in changed:
        echo fmt"   {item.domain}:"
        let fromStr = item.fromVals.join(", ")
        let toStr = item.toVals.join(", ")
        echo fmt"      From: {fromStr}"
        echo fmt"      To:   {toStr}"
      echo ""

    if unchanged.len > 0:
      echo "✓ Unchanged Domains:"
      for item in unchanged:
        let valuesStr = item.values.join(", ")
        echo fmt"   {item.domain}: {valuesStr}"
      echo ""

    # Summary
    let totalDiffs = added.len + removed.len + changed.len
    if totalDiffs == 0:
      echo "✅ Variants are identical"
    else:
      echo fmt"📊 Summary: {totalDiffs} difference(s) found"
      echo fmt"   Added: {added.len}, Removed: {removed.len}, Changed: {changed.len}"

    return 0

proc variantExplainCommand*(domainFlag: string, jsonOutput: bool = false): int =
  ## Explain what a domain flag does
  ## Usage: nip variant explain <domain>.<flag> [--json]
  ##        nip variant explain <domain> [--json]

  initVariantManager()

  # Parse the domain.flag syntax
  let parts = domainFlag.split('.')
  let domain = parts[0]
  let flag = if parts.len > 1: parts[1] else: ""

  # Check if domain exists
  if not SEMANTIC_DOMAINS.hasKey(domain):
    echo fmt"❌ Unknown domain: {domain}"
    echo ""
    echo "Available domains:"
    for d in SEMANTIC_DOMAINS.keys:
      echo fmt"   {d}"
    return 1

  let domainInfo = SEMANTIC_DOMAINS[domain]

  if jsonOutput:
    # JSON output
    var explainJson = %*{
      "domain": domain,
      "description": domainInfo.description,
      "flagType": $domainInfo.flagType,
      "exclusive": domainInfo.exclusive,
      "options": domainInfo.options,
      "default": domainInfo.default
    }

    if flag != "":
      # Specific flag requested
      if flag in domainInfo.options:
        explainJson["flag"] = %*{
          "name": flag,
          "valid": true
        }

        # Add compiler flag effects if available
        if COMPILER_FLAG_RULES.hasKey(domain):
          let rules = COMPILER_FLAG_RULES[domain]
          var effects: seq[string] = @[]
          if rules.hasKey(flag):
            let rule = rules[flag]
            var flagList: seq[string] = @[]
            if rule.cflags.len > 0:
              flagList.add(rule.cflags)
            if rule.ldflags.len > 0:
              flagList.add(rule.ldflags)
            if flagList.len > 0:
              effects.add(flagList.join(" "))
          if effects.len > 0:
            explainJson["flag"]["compilerFlags"] = %effects
      else:
        explainJson["flag"] = %*{
          "name": flag,
          "valid": false,
          "error": "Flag not found in domain options"
        }

    echo explainJson.pretty()
    return 0

  else:
    # Human-readable output
    echo fmt"📖 Domain: {domain}"
    echo ""
    echo fmt"Description:"
    echo fmt"   {domainInfo.description}"
    echo ""
    echo fmt"Type: {domainInfo.flagType}"
    let exclusiveMsg = if domainInfo.exclusive: "Yes (only one value allowed)" else: "No (multiple values allowed)"
    echo fmt"Exclusive: {exclusiveMsg}"
    echo ""

    if domainInfo.options.len > 0:
      echo "Available Options:"
      for opt in domainInfo.options:
        let marker = if opt == domainInfo.default: " (default)" else: ""
        echo fmt"   • {opt}{marker}"
      echo ""

    # If specific flag requested, show details
    if flag != "":
      if flag in domainInfo.options:
        echo fmt"Flag: {flag}"
        echo ""

        # Show compiler flag effects
        if COMPILER_FLAG_RULES.hasKey(domain):
          let rules = COMPILER_FLAG_RULES[domain]
          if rules.hasKey(flag):
            let rule = rules[flag]
            echo "Compiler Flag Effects:"
            if rule.cflags.len > 0:
              echo fmt"   CFLAGS: {rule.cflags}"
            if rule.ldflags.len > 0:
              echo fmt"   LDFLAGS: {rule.ldflags}"
            echo ""

        # Show conflicts (for exclusive domains)
        if domainInfo.exclusive:
          echo "Conflicts With:"
          for opt in domainInfo.options:
            if opt != flag:
              echo fmt"   • {opt}"
          echo ""

        echo "Usage Example:"
        echo fmt"   nip graft <package> +{domain}={flag}"
      else:
        echo fmt"❌ Flag '{flag}' not found in domain '{domain}'"
        echo ""
        echo "Did you mean one of these?"
        for opt in domainInfo.options:
          echo fmt"   • {opt}"
        return 1

    else:
      # Show usage examples for the domain
      echo "Usage Examples:"
      if domainInfo.options.len > 0:
        let exampleFlag = domainInfo.options[0]
        echo fmt"   nip graft <package> +{domain}={exampleFlag}"
        if not domainInfo.exclusive and domainInfo.options.len > 1:
          let exampleFlag2 = domainInfo.options[1]
          echo fmt"   nip graft <package> +{domain}={exampleFlag},{exampleFlag2}"
      echo ""

    return 0


# #############################################################################
# Migration Commands (Task 15)
# #############################################################################

proc migrateFlagsCommand*(
  filePath: string = "",
  dryRun: bool = false,
  createBackup: bool = true,
  outputPath: string = ""
): int =
  ## Migrate legacy USE flags to domain syntax
  ## Usage: nip migrate-flags [options] [file]

  # If no file specified, show help
  if filePath.len == 0:
    printMigrationHelp()
    return 0

  # Check if file exists
  if not fileExists(filePath):
    echo fmt"❌ File not found: {filePath}"
    return 1

  echo fmt"🔄 Migrating flags in: {filePath}"
  echo ""

  if dryRun:
    echo "🔍 DRY RUN - No changes will be made"
    echo ""

  # Read and analyze file
  try:
    let content = readFile(filePath)
    var legacyCount = 0
    var translations: seq[tuple[old: string, new: string]] = @[]

    for line in content.splitLines():
      let trimmed = line.strip()

      # Skip comments and empty lines
      if trimmed.len == 0 or trimmed.startsWith("#"):
        continue

      # Check for legacy syntax
      if isLegacyFlagString(trimmed):
        let translated = translateFlagString(trimmed)
        if translated != trimmed:
          legacyCount += 1
          translations.add((old: trimmed, new: translated))

    if legacyCount == 0:
      echo "✅ No legacy flags found - file is already using new syntax"
      return 0

    # Show translations
    echo fmt"Found {legacyCount} legacy flag(s):"
    echo ""
    for trans in translations:
      echo fmt"  {trans.old}"
      echo fmt"  → {trans.new}"
      echo ""

    if dryRun:
      echo "🔍 Dry run complete - no changes made"
      return 0

    # Create backup if requested (only if not dry-run)
    if createBackup:
      if createMigrationBackup(filePath):
        echo fmt"💾 Backup created: {filePath}.backup"
      else:
        echo "⚠️  Warning: Could not create backup"

    # Perform migration
    let (success, message) = migrateConfigFile(filePath, outputPath)

    if success:
      echo message
      return 0
    else:
      echo fmt"❌ {message}"
      return 1

  except IOError as e:
    echo fmt"❌ Error reading file: {e.msg}"
    return 1

proc checkLegacyFlagsCommand*(flags: seq[string]): int =
  ## Check if flags use legacy syntax and suggest alternatives
  ## Usage: nip check-flags <flags...>

  if flags.len == 0:
    echo "Usage: nip check-flags <flags...>"
    return 1

  echo "🔍 Checking flags for legacy syntax..."
  echo ""

  var hasLegacy = false

  for flagStr in flags:
    if isLegacyFlagString(flagStr):
      let translated = translateFlagString(flagStr)
      if translated != flagStr:
        hasLegacy = true
        echo fmt"⚠️  Legacy: {flagStr}"
        echo fmt"   Suggested: {translated}"
        echo ""

  if not hasLegacy:
    echo "✅ All flags use modern syntax"

  return 0
