## NEXTER Container Removal
##
## **Purpose:**
## Implements atomic removal of NEXTER containers including stopping running
## instances, removing references, cleaning up state, and marking chunks for
## garbage collection.
##
## **Design Principles:**
## - Atomic removal operations
## - Graceful container shutdown
## - Reference cleanup for garbage collection
## - State preservation for recovery
##
## **Requirements:**
## - Requirement 5.3: Remove NEXTER containers
## - Requirement 12.1: Mark chunks for garbage collection

import std/[os, strutils, times, options, tables]
import nip/[nexter_installer, container_management, nexter_manifest]

type
  RemovalResult* = object
    ## Result of NEXTER removal
    success*: bool
    containerName*: string
    removedPath*: string
    chunksMarkedForGC*: int
    error*: string

  RemovalError* = object of CatchableError
    code*: RemovalErrorCode
    context*: string
    suggestions*: seq[string]

  RemovalErrorCode* = enum
    ContainerNotFound,
    ContainerStillRunning,
    RemovalFailed,
    ReferenceCleanupFailed,
    StateCleanupFailed

# ============================================================================
# Container Removal
# ============================================================================

proc removeNEXTER*(containerName: string, storageRoot: string = "",
                   manager: Option[ContainerManager] = none[ContainerManager]()): RemovalResult =
  ## Remove NEXTER container atomically
  ##
  ## **Requirements:**
  ## - Requirement 5.3: Remove container
  ## - Requirement 12.1: Mark chunks for garbage collection
  ##
  ## **Process:**
  ## 1. Stop running container (if any)
  ## 2. Remove manifest and configuration
  ## 3. Remove references from CAS
  ## 4. Mark chunks for garbage collection
  ## 5. Clean up container state
  ##
  ## **Returns:**
  ## - RemovalResult with success status and details
  ##
  ## **Raises:**
  ## - RemovalError if removal fails

  let root = if storageRoot.len > 0: storageRoot else: getHomeDir() / ".local/share/nexus"
  let containerPath = root / "nexters" / containerName
  let casRoot = root / "cas"
  let refsPath = casRoot / "refs" / "nexters" / (containerName & ".refs")

  try:
    # Check if container exists
    if not dirExists(containerPath):
      return RemovalResult(
        success: false,
        containerName: containerName,
        removedPath: containerPath,
        chunksMarkedForGC: 0,
        error: "Container not found at " & containerPath
      )

    # Stop running container if manager provided
    if manager.isSome:
      var mgr = manager.get()
      if isContainerRunning(mgr):
        if not stopContainer(mgr, timeout=10):
          return RemovalResult(
            success: false,
            containerName: containerName,
            removedPath: containerPath,
            chunksMarkedForGC: 0,
            error: "Failed to stop running container"
          )

    # Read references before removal
    var chunksMarkedForGC = 0
    if fileExists(refsPath):
      try:
        let refs = readFile(refsPath).split('\n')
        chunksMarkedForGC = refs.len
      except:
        discard

    # Remove manifest and configuration files
    try:
      let manifestPath = containerPath / "manifest.kdl"
      let environmentPath = containerPath / "environment.kdl"
      let signaturePath = containerPath / "signature.sig"

      if fileExists(manifestPath):
        removeFile(manifestPath)
      if fileExists(environmentPath):
        removeFile(environmentPath)
      if fileExists(signaturePath):
        removeFile(signaturePath)

      # Remove container directory
      removeDir(containerPath)

    except Exception as e:
      return RemovalResult(
        success: false,
        containerName: containerName,
        removedPath: containerPath,
        chunksMarkedForGC: 0,
        error: "Failed to remove container files: " & e.msg
      )

    # Remove references to mark chunks for garbage collection
    try:
      if fileExists(refsPath):
        removeFile(refsPath)
    except Exception as e:
      return RemovalResult(
        success: false,
        containerName: containerName,
        removedPath: containerPath,
        chunksMarkedForGC: 0,
        error: "Failed to remove references: " & e.msg
      )

    return RemovalResult(
      success: true,
      containerName: containerName,
      removedPath: containerPath,
      chunksMarkedForGC: chunksMarkedForGC,
      error: ""
    )

  except Exception as e:
    return RemovalResult(
      success: false,
      containerName: containerName,
      removedPath: containerPath,
      chunksMarkedForGC: 0,
      error: "Removal failed: " & e.msg
    )

# ============================================================================
# Batch Removal
# ============================================================================

proc removeAllNEXTER*(storageRoot: string = ""): seq[RemovalResult] =
  ## Remove all NEXTER containers
  ##
  ## **Requirements:**
  ## - Requirement 5.3: Remove all containers
  ##
  ## **Process:**
  ## 1. List all installed containers
  ## 2. Remove each container
  ## 3. Return results for each removal
  ##
  ## **Returns:**
  ## - Sequence of RemovalResult for each container

  let root = if storageRoot.len > 0: storageRoot else: getHomeDir() / ".local/share/nexus"
  let nextersDir = root / "nexters"

  result = @[]

  if not dirExists(nextersDir):
    return

  try:
    for entry in walkDir(nextersDir):
      if entry.kind == pcDir:
        let containerName = entry.path.extractFilename()
        let removalResult = removeNEXTER(containerName, storageRoot)
        result.add(removalResult)
  except:
    discard

# ============================================================================
# Verification
# ============================================================================

proc verifyRemoval*(containerName: string, storageRoot: string = ""): bool =
  ## Verify container has been removed
  ##
  ## **Requirements:**
  ## - Requirement 5.3: Verify removal
  ##
  ## **Checks:**
  ## 1. Container directory doesn't exist
  ## 2. References file doesn't exist
  ## 3. No manifest files remain

  let root = if storageRoot.len > 0: storageRoot else: getHomeDir() / ".local/share/nexus"
  let containerPath = root / "nexters" / containerName
  let casRoot = root / "cas"
  let refsPath = casRoot / "refs" / "nexters" / (containerName & ".refs")

  # Check container directory
  if dirExists(containerPath):
    return false

  # Check references
  if fileExists(refsPath):
    return false

  return true

# ============================================================================
# Cleanup Utilities
# ============================================================================

proc cleanupOrphanedReferences*(storageRoot: string = ""): int =
  ## Clean up orphaned reference files
  ##
  ## **Requirements:**
  ## - Requirement 12.1: Clean up orphaned references
  ##
  ## **Process:**
  ## 1. List all reference files
  ## 2. Check if corresponding container exists
  ## 3. Remove orphaned references
  ##
  ## **Returns:**
  ## - Number of orphaned references cleaned up

  let root = if storageRoot.len > 0: storageRoot else: getHomeDir() / ".local/share/nexus"
  let nextersDir = root / "nexters"
  let refsDir = root / "cas" / "refs" / "nexters"

  var cleanedCount = 0

  if not dirExists(refsDir):
    return 0

  try:
    for refFile in walkFiles(refsDir / "*.refs"):
      let containerName = refFile.extractFilename().replace(".refs", "")
      let containerPath = nextersDir / containerName

      # If container doesn't exist, remove the reference
      if not dirExists(containerPath):
        try:
          removeFile(refFile)
          cleanedCount += 1
        except:
          discard
  except:
    discard

  return cleanedCount

# ============================================================================
# Formatting
# ============================================================================

proc `$`*(removalResult: RemovalResult): string =
  ## Format removal result as string
  if removalResult.success:
    return "✅ Removed " & removalResult.containerName & " (" & $removalResult.chunksMarkedForGC & " chunks marked for GC)"
  else:
    return "❌ Failed to remove " & removalResult.containerName & ": " & removalResult.error
