## NEXTER Container Removal Tests
##
## Tests for atomic removal of NEXTER containers including stopping,
## reference cleanup, and garbage collection marking.

import std/[unittest, os, tempfiles, options, strutils, times, tables]
import nip/nexter_removal
import nip/nexter_installer
import nip/nexter
import nip/nexter_manifest
import nip/manifest_parser

# Helper to create a test container
proc createTestContainer(name: string, version: string): NEXTERContainer =
  let buildDate = parse("2025-11-28T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
  return NEXTERContainer(
    manifest: NEXTERManifest(
      name: name,
      version: parseSemanticVersion(version),
      buildDate: buildDate,
      metadata: ContainerInfo(
        description: "Test container",
        license: "MIT"
      ),
      provenance: ProvenanceInfo(
        source: "https://example.com/source.tar.gz",
        sourceHash: "xxh3-source-hash",
        buildTimestamp: buildDate
      ),
      buildConfig: BuildConfiguration(
        configureFlags: @[],
        compilerFlags: @[],
        compilerVersion: "gcc-13",
        targetArchitecture: "x86_64",
        libc: "musl",
        allocator: "jemalloc",
        buildSystem: "custom"
      ),
      base: BaseConfig(
        baseImage: some("alpine"),
        baseVersion: some("3.18")
      ),
      environment: initTable[string, string](),
      casChunks: @[],
      namespace: ContainerNamespace(
        isolationType: "full",
        capabilities: @[],
        mounts: @[],
        devices: @[]
      ),
      startup: StartupConfig(
        command: @["/bin/sh"],
        workingDir: "/",
        user: none(string),
        entrypoint: none(string)
      ),
      buildHash: "xxh3-build-hash",
      signature: SignatureInfo(
        algorithm: "ed25519",
        keyId: "test-key",
        signature: "test-sig"
      )
    ),
    environment: "PATH=/usr/bin:/bin",
    chunks: @[
      ChunkData(
        hash: "xxh3-chunk1",
        data: "chunk1 data",
        size: 11,
        chunkType: Binary
      )
    ],
    signature: "ed25519-signature",
    archivePath: ""
  )

suite "NEXTER Container Removal Tests":

  setup:
    let tempDir = createTempDir("nexter_removal_test_", "")
    let storageRoot = tempDir / "storage"
    createDir(storageRoot)

  teardown:
    removeDir(tempDir)

  test "Remove NEXTER container successfully":
    ## Verify container can be removed
    
    let container = createTestContainer("remove-test", "1.0.0")
    let archivePath = storageRoot / "remove-test.nexter"
    
    # Create and install container
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Verify it exists
    check dirExists(storageRoot / "nexters" / "remove-test")
    
    # Remove container
    let result = removeNEXTER("remove-test", storageRoot)
    
    check result.success
    check result.containerName == "remove-test"
    check result.chunksMarkedForGC >= 0

  test "Removal fails for non-existent container":
    ## Verify error handling for missing container
    
    let result = removeNEXTER("nonexistent", storageRoot)
    
    check not result.success
    check "not found" in result.error.toLowerAscii()

  test "Verify removal - container removed":
    ## Verify container is actually removed
    
    let container = createTestContainer("verify-remove", "1.0.0")
    let archivePath = storageRoot / "verify-remove.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Remove container
    discard removeNEXTER("verify-remove", storageRoot)
    
    # Verify removal
    check verifyRemoval("verify-remove", storageRoot)

  test "Verify removal - container still exists":
    ## Verify detection of existing container
    
    let container = createTestContainer("still-exists", "1.0.0")
    let archivePath = storageRoot / "still-exists.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Don't remove, just verify
    check not verifyRemoval("still-exists", storageRoot)

  test "Remove multiple containers":
    ## Verify batch removal works
    
    # Install multiple containers
    for i in 1..3:
      let container = createTestContainer("batch-" & $i, "1.0.0")
      let archivePath = storageRoot / ("batch-" & $i & ".nexter")
      createNEXTER(container.manifest, container.environment, container.chunks,
                   container.signature, archivePath)
      discard installNEXTER(archivePath, storageRoot)
    
    # Remove all
    let results = removeAllNEXTER(storageRoot)
    
    check results.len == 3
    for result in results:
      check result.success

  test "Cleanup orphaned references":
    ## Verify orphaned reference cleanup
    
    let container = createTestContainer("orphan-test", "1.0.0")
    let archivePath = storageRoot / "orphan-test.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Remove container but leave references (simulate orphaned state)
    removeDir(storageRoot / "nexters" / "orphan-test")
    
    # Cleanup orphaned references
    let cleanedCount = cleanupOrphanedReferences(storageRoot)
    
    check cleanedCount >= 0

  test "Format removal result - success":
    ## Verify success result formatting
    
    let result = RemovalResult(
      success: true,
      containerName: "test",
      removedPath: "/path/to/test",
      chunksMarkedForGC: 5,
      error: ""
    )
    
    let formatted = $result
    
    check "✅" in formatted
    check "test" in formatted
    check "5" in formatted

  test "Format removal result - failure":
    ## Verify failure result formatting
    
    let result = RemovalResult(
      success: false,
      containerName: "test",
      removedPath: "/path/to/test",
      chunksMarkedForGC: 0,
      error: "Test error"
    )
    
    let formatted = $result
    
    check "❌" in formatted
    check "test" in formatted
    check "Test error" in formatted

suite "NEXTER Removal Property Tests":

  test "Property: Successful removal marks chunks for GC":
    ## Verify chunks are marked for garbage collection
    
    let tempDir = createTempDir("nexter_prop_", "")
    defer: removeDir(tempDir)
    let storageRoot = tempDir / "storage"
    createDir(storageRoot)
    
    let container = createTestContainer("prop-gc", "1.0.0")
    let archivePath = storageRoot / "prop-gc.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    let result = removeNEXTER("prop-gc", storageRoot)
    
    if result.success:
      check result.chunksMarkedForGC >= 0

  test "Property: Removal is idempotent":
    ## Verify removing twice doesn't cause issues
    
    let tempDir = createTempDir("nexter_prop_", "")
    defer: removeDir(tempDir)
    let storageRoot = tempDir / "storage"
    createDir(storageRoot)
    
    let container = createTestContainer("prop-idem", "1.0.0")
    let archivePath = storageRoot / "prop-idem.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Remove twice
    let result1 = removeNEXTER("prop-idem", storageRoot)
    let result2 = removeNEXTER("prop-idem", storageRoot)
    
    check result1.success
    check not result2.success  # Second removal should fail (already removed)

  test "Property: Verification matches actual state":
    ## Verify removal verification is accurate
    
    let tempDir = createTempDir("nexter_prop_", "")
    defer: removeDir(tempDir)
    let storageRoot = tempDir / "storage"
    createDir(storageRoot)
    
    let container = createTestContainer("prop-verify", "1.0.0")
    let archivePath = storageRoot / "prop-verify.nexter"
    
    createNEXTER(container.manifest, container.environment, container.chunks,
                 container.signature, archivePath)
    discard installNEXTER(archivePath, storageRoot)
    
    # Before removal
    check not verifyRemoval("prop-verify", storageRoot)
    
    # After removal
    discard removeNEXTER("prop-verify", storageRoot)
    check verifyRemoval("prop-verify", storageRoot)
