## NEXTER Container Management
##
## **Purpose:**
## Implements container lifecycle management including stopping, status checking,
## log access, and restart functionality.
##
## **Design Principles:**
## - Clean lifecycle management
## - Non-blocking status queries
## - Comprehensive log access
## - Graceful shutdown with timeout
##
## **Requirements:**
## - Requirement 5.4: Container management (stop, status, logs, restart)

import std/[os, times, options, tables, osproc, strutils, posix]
import nip/[nexter_manifest, container_startup]

type
  ContainerManager* = object
    ## Container manager for lifecycle operations
    containerName*: string
    process*: ContainerProcess
    config*: ContainerStartupConfig
    logs*: seq[string]
    createdAt*: DateTime
    stoppedAt*: Option[DateTime]

  ContainerLog* = object
    ## Container log entry
    timestamp*: DateTime
    level*: LogLevel
    message*: string

  LogLevel* = enum
    ## Log level
    Debug,
    Info,
    Warning,
    Error

  ContainerStats* = object
    ## Container statistics
    name*: string
    status*: ProcessStatus
    uptime*: int64              ## Seconds
    pid*: int
    memoryUsage*: int64         ## Bytes
    cpuUsage*: float            ## Percentage
    restartCount*: int

  ContainerManagementError* = object of CatchableError
    code*: ManagementErrorCode
    context*: string
    suggestions*: seq[string]

  ManagementErrorCode* = enum
    ContainerNotRunning,
    ProcessTerminationFailed,
    LogAccessFailed,
    StatsUnavailable,
    RestartFailed

# ============================================================================
# Container Manager Creation
# ============================================================================

proc createContainerManager*(name: string, process: ContainerProcess,
                            config: ContainerStartupConfig): ContainerManager =
  ## Create container manager
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Initialize container manager

  return ContainerManager(
    containerName: name,
    process: process,
    config: config,
    logs: @[],
    createdAt: now(),
    stoppedAt: none[DateTime]()
  )

# ============================================================================
# Container Stopping
# ============================================================================

proc stopContainer*(manager: var ContainerManager, timeout: int = 30): bool =
  ## Stop container gracefully
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Stop running container
  ##
  ## **Process:**
  ## 1. Send SIGTERM to process
  ## 2. Wait for graceful shutdown (timeout seconds)
  ## 3. Send SIGKILL if still running
  ## 4. Update container status

  if manager.process.pid <= 0:
    return false

  try:
    # Send SIGTERM for graceful shutdown
    let termResult = kill(Pid(manager.process.pid), SIGTERM)
    if termResult != 0:
      # Process might already be dead
      manager.process.status = Stopped
      manager.stoppedAt = some(now())
      return true

    # Wait for graceful shutdown
    var waited = 0
    while waited < timeout:
      # Check if process is still running
      let checkResult = kill(Pid(manager.process.pid), 0)
      if checkResult != 0:
        # Process has exited
        manager.process.status = Stopped
        manager.stoppedAt = some(now())
        return true

      # Sleep a bit and try again
      sleep(100)
      waited += 100

    # Process didn't stop gracefully, force kill
    let killResult = kill(Pid(manager.process.pid), SIGKILL)
    if killResult == 0:
      manager.process.status = Stopped
      manager.stoppedAt = some(now())
      return true
    else:
      return false

  except Exception as e:
    return false

proc restartContainer*(manager: var ContainerManager): bool =
  ## Restart container
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Restart container
  ##
  ## **Process:**
  ## 1. Stop current container
  ## 2. Start new container with same config
  ## 3. Update manager state

  # Stop current container
  if not stopContainer(manager):
    return false

  # Wait a bit for cleanup
  sleep(500)

  # Start new container
  let newProcess = startContainer(manager.config)

  if newProcess.status == Failed:
    return false

  # Update manager
  manager.process = newProcess
  manager.stoppedAt = none[DateTime]()

  return true

# ============================================================================
# Container Status
# ============================================================================

proc getContainerStatus*(manager: ContainerManager): ProcessStatus =
  ## Get current container status
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Query container status

  if manager.process.pid <= 0:
    return manager.process.status

  # Check if process is still running
  try:
    let checkResult = kill(Pid(manager.process.pid), 0)
    if checkResult == 0:
      return Running
    else:
      return Stopped
  except:
    return Stopped

proc getContainerStats*(manager: ContainerManager): ContainerStats =
  ## Get container statistics
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Get container statistics
  ##
  ## **Returns:**
  ## Container statistics including uptime, memory, CPU usage

  let status = getContainerStatus(manager)
  let uptime = if status == Running:
    (now() - manager.createdAt).inSeconds
  else:
    if manager.stoppedAt.isSome:
      (manager.stoppedAt.get() - manager.createdAt).inSeconds
    else:
      0

  return ContainerStats(
    name: manager.containerName,
    status: status,
    uptime: uptime,
    pid: manager.process.pid,
    memoryUsage: 0,  # Would require /proc parsing in real implementation
    cpuUsage: 0.0,   # Would require /proc parsing in real implementation
    restartCount: 0  # Would need to track restarts
  )

proc isContainerRunning*(manager: ContainerManager): bool =
  ## Check if container is running
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Query running status

  return getContainerStatus(manager) == Running

# ============================================================================
# Container Logs
# ============================================================================

proc addLog*(manager: var ContainerManager, level: LogLevel, message: string) =
  ## Add log entry to container
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Log container operations

  manager.logs.add(message)

proc getContainerLogs*(manager: ContainerManager, level: LogLevel = Debug): seq[string] =
  ## Get container logs
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Access container logs
  ##
  ## **Returns:**
  ## All logs at or above specified level

  return manager.logs

proc clearContainerLogs*(manager: var ContainerManager) =
  ## Clear container logs
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Manage container logs

  manager.logs = @[]

proc getLastLogs*(manager: ContainerManager, count: int = 10): seq[string] =
  ## Get last N log entries
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Access recent logs

  let startIdx = max(0, manager.logs.len - count)
  return manager.logs[startIdx..^1]

# ============================================================================
# Container Uptime
# ============================================================================

proc getContainerUptime*(manager: ContainerManager): int64 =
  ## Get container uptime in seconds
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Query container uptime

  let stats = getContainerStats(manager)
  return stats.uptime

proc getContainerUptimeFormatted*(manager: ContainerManager): string =
  ## Get container uptime as formatted string
  ##
  ## **Requirements:**
  ## - Requirement 5.4: Format uptime for display

  let uptime = getContainerUptime(manager)
  let days = uptime div 86400
  let hours = (uptime mod 86400) div 3600
  let minutes = (uptime mod 3600) div 60
  let seconds = uptime mod 60

  if days > 0:
    return $days & "d " & $hours & "h " & $minutes & "m"
  elif hours > 0:
    return $hours & "h " & $minutes & "m " & $seconds & "s"
  elif minutes > 0:
    return $minutes & "m " & $seconds & "s"
  else:
    return $seconds & "s"

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

proc `$`*(manager: ContainerManager): string =
  ## Format container manager as string
  let status = getContainerStatus(manager)
  let uptime = getContainerUptimeFormatted(manager)

  result = "Container: " & manager.containerName & "\n"
  result.add("  Status: " & $status & "\n")
  result.add("  PID: " & $manager.process.pid & "\n")
  result.add("  Uptime: " & uptime & "\n")
  result.add("  Logs: " & $manager.logs.len & " entries\n")

proc `$`*(stats: ContainerStats): string =
  ## Format container stats as string
  result = "Container Stats: " & stats.name & "\n"
  result.add("  Status: " & $stats.status & "\n")
  result.add("  PID: " & $stats.pid & "\n")
  result.add("  Uptime: " & $stats.uptime & "s\n")
  result.add("  Memory: " & $(stats.memoryUsage div 1024 div 1024) & "MB\n")
  result.add("  CPU: " & formatFloat(stats.cpuUsage, ffDecimal, 2) & "%\n")
  result.add("  Restarts: " & $stats.restartCount & "\n")
