## NEXTER Container Startup Tests
##
## Tests for container startup and lifecycle management.
## Verifies configuration validation, process setup, and execution.

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

# Helper to create a test manifest
proc createTestManifest(name: string, version: string): NEXTERManifest =
  let buildDate = parse("2025-11-28T12:00:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'")
  return 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"
    )
  )

suite "Container Startup Configuration Tests":

  test "Create startup config from manifest":
    ## Verify startup config can be created from manifest
    
    let manifest = createTestManifest("startup-test", "1.0.0")
    let config = createStartupConfig(manifest)
    
    check config.command.len > 0
    check config.workingDir == "/"
    check config.user.isNone

  test "Startup config with custom command":
    ## Verify custom command is preserved
    
    let manifest = createTestManifest("cmd-test", "1.0.0")
    var cmdManifest = manifest
    cmdManifest.startup.command = @["/bin/bash", "-c", "echo hello"]
    
    let config = createStartupConfig(cmdManifest)
    
    check config.command.len == 3
    check config.command[0] == "/bin/bash"
    check config.command[1] == "-c"
    check config.command[2] == "echo hello"

  test "Startup config with working directory":
    ## Verify working directory is preserved
    
    let manifest = createTestManifest("workdir-test", "1.0.0")
    var workdirManifest = manifest
    workdirManifest.startup.workingDir = "/app"
    
    let config = createStartupConfig(workdirManifest)
    
    check config.workingDir == "/app"

  test "Startup config with user":
    ## Verify user is preserved
    
    let manifest = createTestManifest("user-test", "1.0.0")
    var userManifest = manifest
    userManifest.startup.user = some("appuser")
    
    let config = createStartupConfig(userManifest)
    
    check config.user.isSome
    check config.user.get() == "appuser"

  test "Startup config with entrypoint":
    ## Verify entrypoint is preserved
    
    let manifest = createTestManifest("entry-test", "1.0.0")
    var entryManifest = manifest
    entryManifest.startup.entrypoint = some("/app/entrypoint.sh")
    
    let config = createStartupConfig(entryManifest)
    
    check config.entrypoint.isSome
    check config.entrypoint.get() == "/app/entrypoint.sh"

  test "Startup config with environment":
    ## Verify environment variables are preserved
    
    let manifest = createTestManifest("env-test", "1.0.0")
    var envManifest = manifest
    envManifest.environment["APP_ENV"] = "production"
    envManifest.environment["DEBUG"] = "false"
    
    let config = createStartupConfig(envManifest)
    
    check config.environment["APP_ENV"] == "production"
    check config.environment["DEBUG"] == "false"

suite "Container Startup Validation Tests":

  test "Validate valid startup config":
    ## Verify valid config passes validation
    
    let config = ContainerStartupConfig(
      command: @["/bin/sh"],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    check validateStartupConfig(config)

  test "Validate fails for empty command":
    ## Verify validation fails for empty command
    
    let config = ContainerStartupConfig(
      command: @[],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    check not validateStartupConfig(config)

  test "Validate fails for empty user":
    ## Verify validation fails for empty user
    
    let config = ContainerStartupConfig(
      command: @["/bin/sh"],
      workingDir: "/",
      user: some(""),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    check not validateStartupConfig(config)

  test "Validate fails for empty entrypoint":
    ## Verify validation fails for empty entrypoint
    
    let config = ContainerStartupConfig(
      command: @["/bin/sh"],
      workingDir: "/",
      user: none(string),
      entrypoint: some(""),
      environment: initTable[string, string]()
    )
    
    check not validateStartupConfig(config)

suite "Container Process Tests":

  test "Start container with valid config":
    ## Verify container can be started
    
    let config = ContainerStartupConfig(
      command: @["/bin/echo", "hello"],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    let process = startContainer(config)
    
    # Process should be created (or fail gracefully)
    check process.startTime <= now()

  test "Start container with invalid config":
    ## Verify container startup fails gracefully for invalid config
    
    let config = ContainerStartupConfig(
      command: @[],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    let process = startContainer(config)
    
    check process.status == Failed
    check process.pid == -1

  test "Get container logs":
    ## Verify logs can be retrieved
    
    let process = ContainerProcess(
      pid: 1234,
      startTime: now(),
      status: Running,
      exitCode: none[int](),
      output: "stdout output",
      error: "stderr output"
    )
    
    let logs = getContainerLogs(process)
    
    check "stdout output" in logs
    check "stderr output" in logs

  test "Get container status":
    ## Verify status can be retrieved
    
    let process = ContainerProcess(
      pid: 1234,
      startTime: now(),
      status: Running,
      exitCode: none[int](),
      output: "",
      error: ""
    )
    
    let status = getContainerStatus(process)
    
    check status == Running

  test "Format startup config":
    ## Verify startup config formatting
    
    let config = ContainerStartupConfig(
      command: @["/bin/sh"],
      workingDir: "/app",
      user: some("appuser"),
      entrypoint: some("/app/start.sh"),
      environment: initTable[string, string]()
    )
    
    let formatted = $config
    
    check "Container Startup Config" in formatted
    check "/bin/sh" in formatted
    check "/app" in formatted
    check "appuser" in formatted

  test "Format container process":
    ## Verify container process formatting
    
    let process = ContainerProcess(
      pid: 1234,
      startTime: now(),
      status: Running,
      exitCode: none[int](),
      output: "",
      error: ""
    )
    
    let formatted = $process
    
    check "Container Process" in formatted
    check "1234" in formatted
    check "Running" in formatted

suite "Container Startup Property Tests":

  test "Property: Config preserves all manifest startup settings":
    ## Verify all startup settings are preserved
    
    let manifest = createTestManifest("prop-startup", "1.0.0")
    var testManifest = manifest
    testManifest.startup.command = @["/bin/bash", "-c", "test"]
    testManifest.startup.workingDir = "/test"
    testManifest.startup.user = some("testuser")
    testManifest.startup.entrypoint = some("/test/entry.sh")
    
    let config = createStartupConfig(testManifest)
    
    check config.command == @["/bin/bash", "-c", "test"]
    check config.workingDir == "/test"
    check config.user.get() == "testuser"
    check config.entrypoint.get() == "/test/entry.sh"

  test "Property: Process has valid start time":
    ## Verify process start time is set correctly
    
    let beforeStart = now()
    let config = ContainerStartupConfig(
      command: @["/bin/echo"],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    let process = startContainer(config)
    let afterStart = now()
    
    check process.startTime >= beforeStart
    check process.startTime <= afterStart

  test "Property: Failed process has negative PID":
    ## Verify failed processes have negative PID
    
    let config = ContainerStartupConfig(
      command: @[],
      workingDir: "/",
      user: none(string),
      entrypoint: none(string),
      environment: initTable[string, string]()
    )
    
    let process = startContainer(config)
    
    if process.status == Failed:
      check process.pid < 0

  test "Property: Valid config always validates":
    ## Verify valid configs always pass validation
    
    for i in 1..10:
      let config = ContainerStartupConfig(
        command: @["/bin/sh", "-c", "echo test-" & $i],
        workingDir: "/",
        user: none(string),
        entrypoint: none(string),
        environment: initTable[string, string]()
      )
      
      check validateStartupConfig(config)
