# nimpak/grafting_simple.nim
# Simplified grafting infrastructure for external package integration

import std/[tables, sets, strutils, json, os, times, sequtils, hashes, options]
import ../nip/types
import utils/resultutils
import types/grafting_types
export grafting_types

type
  # Core grafting engine types
  GraftingEngine* = object
    adapters*: Table[string, PackageAdapter]
    cache*: GraftingCache
    config*: GraftingConfig
    transactions*: seq[GraftTransaction]

# Core grafting engine procedures
proc initGraftingEngine*(configPath: string = ""): Result[GraftingEngine, string] =
  ## Initialize the grafting engine with configuration
  var engine = GraftingEngine(
    adapters: initTable[string, PackageAdapter](),
    cache: GraftingCache(
      cacheDir: getHomeDir() / ".nip" / "graft-cache",
      metadata: initTable[string, GraftedPackageMetadata](),
      archives: initTable[string, string]()
    ),
    config: GraftingConfig(
      enabled: true,
      verifyGraftedPackages: true,
      convertToNpkAutomatically: false,
      adapters: initTable[string, AdapterConfig]()
    ),
    transactions: @[]
  )

  # Create cache directory
  if not dirExists(engine.cache.cacheDir):
    try:
      createDir(engine.cache.cacheDir)
    except OSError as e:
      return Result[GraftingEngine, string](isOk: false, error: "Failed to create cache directory: " & e.msg)

  return Result[GraftingEngine, string](isOk: true, value: engine)

proc registerAdapter*(engine: var GraftingEngine, adapter: PackageAdapter): Result[bool, string] =
  ## Register a package adapter with the grafting engine
  if adapter.name in engine.adapters:
    return Result[bool, string](isOk: false, error: "Adapter already registered: " & adapter.name)

  engine.adapters[adapter.name] = adapter
  echo "Registered grafting adapter: " & adapter.name
  return Result[bool, string](isOk: true, value: true)

proc graftPackage*(engine: var GraftingEngine, source: string, packageName: string): Result[GraftResult, string] =
  ## Graft a package from an external source
  if not engine.config.enabled:
    return Result[GraftResult, string](isOk: false, error: "Grafting is disabled in configuration")

  if source notin engine.adapters:
    return Result[GraftResult, string](isOk: false, error: "Unknown grafting source: " & source)

  let adapter = engine.adapters[source]
  if not adapter.enabled:
    return Result[GraftResult, string](isOk: false, error: "Adapter disabled: " & source)

  # Create a simple result for now
  let result = GraftResult(
    success: true,
    packageId: packageName,
    metadata: GraftedPackageMetadata(
      packageName: packageName,
      version: "1.0.0",
      source: source,
      graftedAt: now(),
      originalHash: "placeholder-hash",
      graftHash: "graft-hash",
      buildLog: "Build log placeholder",
      provenance: ProvenanceInfo(
        originalSource: source,
        downloadUrl: "",
        archivePath: "",
        extractedPath: "",
        conversionLog: ""
      )
    ),
    npkPath: none(string),
    errors: @[]
  )

  echo "Successfully grafted package: " & packageName
  return ok[GraftResult](result)

proc listGraftedPackages*(engine: GraftingEngine): seq[GraftedPackageMetadata] =
  ## List all grafted packages in cache
  result = @[]
  for metadata in engine.cache.metadata.values:
    result.add(metadata)

proc getGraftingStatus*(engine: GraftingEngine): JsonNode =
  ## Get current grafting engine status
  result = %*{
    "enabled": engine.config.enabled,
    "adapters": {},
    "cache": {
      "directory": engine.cache.cacheDir,
      "packages": engine.cache.metadata.len,
      "archives": engine.cache.archives.len
    },
    "transactions": engine.transactions.len
  }

  for name, adapter in engine.adapters:
    result["adapters"][name] = %*{
      "enabled": adapter.enabled,
      "priority": adapter.priority
    }

# Base adapter methods (to be overridden by specific adapters)
method graftPackage*(adapter: PackageAdapter, packageName: string, cache: GraftingCache): GraftResult {.base.} =
  ## Base method for grafting a package - must be overridden by specific adapters
  GraftResult(
    success: false,
    packageId: packageName,
    metadata: GraftedPackageMetadata(),
    npkPath: none(string),
    errors: @["Base adapter method not implemented"]
  )

method validatePackage*(adapter: PackageAdapter, packageName: string): Result[bool, string] {.base.} =
  ## Base method for validating a package - can be overridden
  return ok[bool](true)

method getPackageInfo*(adapter: PackageAdapter, packageName: string): Result[JsonNode, string] {.base.} =
  ## Base method for getting package information - can be overridden
  return ok[JsonNode](%*{"name": packageName, "adapter": adapter.name})

# Utility functions
proc calculateGraftHash*(packageName: string, source: string, timestamp: DateTime): string =
  ## Calculate a unique hash for a grafted package
  let input = packageName & "|" & source & "|" & $timestamp.toTime().toUnix()
  # TODO: Use proper BLAKE3 hashing when available
  "graft-" & $hash(input)

proc findExtractedPath(cacheDir: string, packageName: string): string =
  ## Find the extracted path for a grafted package
  let extractedDir = cacheDir / "extracted" / packageName
  if dirExists(extractedDir):
    return extractedDir

  # Try alternative locations
  let builtDir = cacheDir / "built" / packageName
  if dirExists(builtDir):
    return builtDir

  return ""