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

# graft_coordinator.nim
# Coordinates grafting from adapters and installation
# Ties together adapters + install_manager for unified grafting

import std/[strformat, strutils, json, os]
import install_manager, simple_db, config
import adapters/[nix, pacman, pkgsrc, aur]
import grafting  # For GraftResult type
import types

type
  GraftCoordinator* = ref object
    installManager*: InstallManager
    nixAdapter*: NixAdapter
    pkgsrcAdapter*: PKGSRCAdapter
    pacmanAdapter*: PacmanAdapter  # Specific type
    aurAdapter*: AURAdapter  # AUR adapter
    verbose*: bool

  GraftSource* = enum
    Auto, Nix, PKGSRC, Pacman, AUR

  UnifiedGraftResult* = object
    success*: bool
    packageName*: string
    version*: string
    source*: string
    installPath*: string
    errors*: seq[string]
    warnings*: seq[string]

proc newGraftCoordinator*(config: InstallConfig, verbose: bool = false): GraftCoordinator =
  ## Create a new graft coordinator

  # Create adapter configs with proper cache directories
  var nixConfig = %* {"cache_dir": config.cacheDir / "nix"}
  var pkgsrcConfig = %* {"cache_dir": config.cacheDir / "pkgsrc"}
  var aurConfig = %* {"cache_dir": config.cacheDir / "aur"}

  result = GraftCoordinator(
    installManager: newInstallManager(config),
    nixAdapter: newNixAdapter(nixConfig),
    pkgsrcAdapter: newPKGSRCAdapter(pkgsrcConfig),
    pacmanAdapter: initPacmanAdapter(
      pacmanDbPath = "/var/lib/pacman/local",
      nipDbPath = config.cacheDir / "pacman"
    ),
    aurAdapter: newAURAdapter(aurConfig),
    verbose: verbose
  )

  # Ensure all required directories exist
  result.installManager.ensureDirectories()

proc detectSource*(coordinator: GraftCoordinator, packageName: string): GraftSource =
  ## Auto-detect best source for a package
  # Priority: Nix > PKGSRC > Pacman

  # Try Nix first
  let nixValidation = coordinator.nixAdapter.validatePackage(packageName)
  if nixValidation.isOk and nixValidation.get:
    return GraftSource.Nix

  # Try PKGSRC
  let pkgsrcValidation = coordinator.pkgsrcAdapter.validatePackage(packageName)
  if pkgsrcValidation.isOk and pkgsrcValidation.get:
    return GraftSource.PKGSRC

  # Try Pacman
  let pacmanValidation = coordinator.pacmanAdapter.validatePackage(packageName)
  if pacmanValidation.isOk and pacmanValidation.get:
    return GraftSource.Pacman

  # Default to Nix
  return GraftSource.Nix

proc graftFromNix*(coordinator: GraftCoordinator,
                   packageName: string): UnifiedGraftResult =
  ## Graft a package from Nix
  var result = UnifiedGraftResult(
    success: false,
    packageName: packageName,
    source: "nix"
  )

  if coordinator.verbose:
    echo fmt"🌱 Grafting {packageName} from Nix..."

  # Create temporary cache for grafting
  let cache = GraftingCache(
    cacheDir: coordinator.installManager.config.cacheDir / "nix"
  )
  createDir(cache.cacheDir)

  # Graft from Nix
  let graftResult = coordinator.nixAdapter.graftPackage(packageName, cache)

  if not graftResult.success:
    result.errors = graftResult.errors
    return result

  # Extract version and metadata
  result.version = graftResult.metadata.version
  let metadata = %*{
    "description": graftResult.metadata.packageName,
    "version": graftResult.metadata.version,
    "source": "nix",
    "graft_hash": graftResult.metadata.graftHash
  }

  # Install to system
  # TODO: Pass actual variant descriptor from graft command
  let installResult = coordinator.installManager.installPackage(
    packageName = packageName,
    version = result.version,
    source = "nix",
    sourcePath = graftResult.metadata.provenance.extractedPath,
    graftHash = graftResult.metadata.graftHash,
    metadata = metadata,
    variantDescriptor = "default"  # Will be replaced with actual variants
  )

  if not installResult.success:
    result.errors = installResult.errors
    result.warnings = installResult.warnings
    return result

  result.success = true
  result.installPath = installResult.installPath
  result.warnings = installResult.warnings

  if coordinator.verbose:
    echo fmt"✅ Successfully grafted {packageName} from Nix"
    echo fmt"📍 Installed to: {result.installPath}"

  result

proc graftFromPKGSRC*(coordinator: GraftCoordinator,
                      packageName: string): UnifiedGraftResult =
  ## Graft a package from PKGSRC
  var result = UnifiedGraftResult(
    success: false,
    packageName: packageName,
    source: "pkgsrc"
  )

  if coordinator.verbose:
    echo fmt"🌱 Grafting {packageName} from PKGSRC..."

  # Create temporary cache for grafting
  let cache = GraftingCache(
    cacheDir: coordinator.installManager.config.cacheDir / "pkgsrc"
  )
  createDir(cache.cacheDir)

  # Graft from PKGSRC
  let graftResult = coordinator.pkgsrcAdapter.graftPackage(packageName, cache)

  if not graftResult.success:
    result.errors = graftResult.errors
    return result

  # Extract version and metadata
  result.version = graftResult.metadata.version
  let metadata = %*{
    "description": graftResult.metadata.packageName,
    "version": graftResult.metadata.version,
    "source": "pkgsrc",
    "graft_hash": graftResult.metadata.graftHash
  }

  # Install to system
  # TODO: Pass actual variant descriptor from graft command
  let installResult = coordinator.installManager.installPackage(
    packageName = packageName,
    version = result.version,
    source = "pkgsrc",
    sourcePath = graftResult.metadata.provenance.extractedPath,
    graftHash = graftResult.metadata.graftHash,
    metadata = metadata,
    variantDescriptor = "default"  # Will be replaced with actual variants
  )

  if not installResult.success:
    result.errors = installResult.errors
    result.warnings = installResult.warnings
    return result

  result.success = true
  result.installPath = installResult.installPath
  result.warnings = installResult.warnings

  if coordinator.verbose:
    echo fmt"✅ Successfully grafted {packageName} from PKGSRC"
    echo fmt"📍 Installed to: {result.installPath}"

  result

proc graftFromPacman*(coordinator: GraftCoordinator,
                      packageName: string): UnifiedGraftResult =
  ## Graft a package from Pacman
  var result = UnifiedGraftResult(
    success: false,
    packageName: packageName,
    source: "pacman"
  )

  if coordinator.verbose:
    echo fmt"🌱 Grafting {packageName} from Pacman..."

  # Create temporary cache for grafting
  let cache = GraftingCache(
    cacheDir: coordinator.installManager.config.cacheDir / "pacman"
  )
  createDir(cache.cacheDir)

  # Graft from Pacman
  let graftResult = coordinator.pacmanAdapter.graftPackage(packageName, cache)

  if not graftResult.success:
    result.errors = graftResult.errors
    return result

  # Extract version and metadata
  result.version = graftResult.metadata.version
  let metadata = %*{
    "description": graftResult.metadata.packageName,
    "version": graftResult.metadata.version,
    "source": "pacman",
    "graft_hash": graftResult.metadata.graftHash
  }

  # Install to system
  let installResult = coordinator.installManager.installPackage(
    packageName = packageName,
    version = result.version,
    source = "pacman",
    sourcePath = graftResult.metadata.provenance.extractedPath,
    graftHash = graftResult.metadata.graftHash,
    metadata = metadata,
    variantDescriptor = "default"
  )

  if not installResult.success:
    result.errors = installResult.errors
    result.warnings = installResult.warnings
    return result

  result.success = true
  result.installPath = installResult.installPath
  result.warnings = installResult.warnings

  if coordinator.verbose:
    echo fmt"✅ Successfully grafted {packageName} from Pacman"
    echo fmt"📍 Installed to: {result.installPath}"

  result

proc graftFromAUR*(coordinator: GraftCoordinator,
                     packageName: string): UnifiedGraftResult =
  ## Graft a package from AUR
  var result = UnifiedGraftResult(
    success: false,
    packageName: packageName,
    source: "aur"
  )

  if coordinator.verbose:
    echo fmt"🌱 Grafting {packageName} from AUR..."

  # Create temporary cache for grafting
  let cache = GraftingCache(
    cacheDir: coordinator.installManager.config.cacheDir / "aur"
  )
  createDir(cache.cacheDir)

  # Graft from AUR
  let graftResult = coordinator.aurAdapter.graftPackage(packageName, cache)

  if not graftResult.success:
    result.errors = graftResult.errors
    return result

  # Extract version and metadata
  result.version = graftResult.metadata.version
  let metadata = %*{
    "description": graftResult.metadata.packageName,
    "version": graftResult.metadata.version,
    "source": "aur",
    "graft_hash": graftResult.metadata.graftHash
  }

  # Install to system
  let installResult = coordinator.installManager.installPackage(
    packageName = packageName,
    version = result.version,
    source = "aur",
    sourcePath = graftResult.metadata.provenance.extractedPath,
    graftHash = graftResult.metadata.graftHash,
    metadata = metadata,
    variantDescriptor = "default"
  )

  if not installResult.success:
    result.errors = installResult.errors
    result.warnings = installResult.warnings
    return result

  result.success = true
  result.installPath = installResult.installPath
  result.warnings = installResult.warnings

  if coordinator.verbose:
    echo fmt"✅ Successfully grafted {packageName} from AUR"
    echo fmt"📍 Installed to: {result.installPath}"

  result

proc graft*(coordinator: GraftCoordinator,
            packageName: string,
            source: GraftSource = Auto): UnifiedGraftResult =
  ## Graft a package from specified or auto-detected source

  # Determine source
  var actualSource = source
  if source == Auto:
    actualSource = coordinator.detectSource(packageName)
    if coordinator.verbose:
      echo fmt"🔍 Auto-detected source: {actualSource}"

  # Graft from appropriate source
  case actualSource
  of Nix:
    return coordinator.graftFromNix(packageName)
  of PKGSRC:
    return coordinator.graftFromPKGSRC(packageName)
  of Pacman:
    return coordinator.graftFromPacman(packageName)
  of AUR:
    return coordinator.graftFromAUR(packageName)
  of Auto:
    # Should not reach here
    var result = UnifiedGraftResult(success: false, packageName: packageName)
    result.errors.add("Failed to detect source")
    return result

proc remove*(coordinator: GraftCoordinator, packageName: string): UnifiedGraftResult =
  ## Remove an installed package
  var result = UnifiedGraftResult(
    success: false,
    packageName: packageName
  )

  let removeResult = coordinator.installManager.removePackage(packageName)

  result.success = removeResult.success
  result.version = removeResult.version
  result.errors = removeResult.errors
  result.warnings = removeResult.warnings

  result

proc list*(coordinator: GraftCoordinator, source: string = ""): seq[InstalledPackage] =
  ## List installed packages, optionally filtered by source
  let allPackages = coordinator.installManager.listInstalled()

  if source == "":
    return allPackages

  result = @[]
  for pkg in allPackages:
    if pkg.source == source:
      result.add(pkg)

proc info*(coordinator: GraftCoordinator, packageName: string): InstalledPackage =
  ## Get information about an installed package
  coordinator.installManager.getInstalledPackage(packageName)

proc isInstalled*(coordinator: GraftCoordinator, packageName: string): bool =
  ## Check if a package is installed
  coordinator.installManager.isInstalled(packageName)

proc status*(coordinator: GraftCoordinator): JsonNode =
  ## Get system status
  coordinator.installManager.getStatus()

proc parsePackageSpec*(spec: string): tuple[source: GraftSource, name: string] =
  ## Parse package specification like "nix:firefox" or "pkgsrc:vim"
  if ":" in spec:
    let parts = spec.split(":", 1)
    let sourceStr = parts[0].toLower()
    let name = parts[1]

    let source = case sourceStr
      of "nix": GraftSource.Nix
      of "pkgsrc": GraftSource.PKGSRC
      of "pacman": GraftSource.Pacman
      of "aur": GraftSource.AUR
      else: GraftSource.Auto

    return (source, name)
  else:
    return (Auto, spec)
