## Tests for platform detection and isolation strategy selection
##
## Property-Based Tests:
## - Property 14: Platform Detection Accuracy
## - Property 15: Strategy Selection Correctness
## - Property 16: Mode Selection Logic

import std/[unittest, options]
import ../src/nip/platform

suite "Platform Detection":

  test "detectOSType returns valid OS type":
    ## Test that OS type detection returns a valid value
    let osType = detectOSType()
    check osType in [Linux, FreeBSD, OpenBSD, NetBSD, macOS, Embedded]

  test "getOSTypeString returns non-empty string":
    ## Test that OS type string conversion works
    let osType = detectOSType()
    let osStr = getOSTypeString(osType)
    check osStr.len > 0

  test "getKernelVersion returns non-empty string":
    ## Test that kernel version detection works
    let version = getKernelVersion()
    check version.len > 0
    check version != "unknown" or true  # May be unknown on some systems

  test "parseKernelVersion handles valid version strings":
    ## Test kernel version parsing
    let (major, minor, patch) = parseKernelVersion("5.10.0")
    check major == 5
    check minor == 10
    check patch == 0

  test "parseKernelVersion handles version with suffix":
    ## Test kernel version parsing with suffix
    let (major, minor, patch) = parseKernelVersion("5.10.0-generic")
    check major == 5
    check minor == 10
    check patch == 0

  test "parseKernelVersion handles incomplete versions":
    ## Test kernel version parsing with incomplete version
    let (major, minor, patch) = parseKernelVersion("5.10")
    check major == 5
    check minor == 10
    check patch == 0

  test "getMemoryTotal returns positive value":
    ## Test that memory detection returns a positive value
    let memory = getMemoryTotal()
    check memory >= 0  # May be 0 on some systems

  test "getCPUCount returns positive value":
    ## Test that CPU count detection returns a positive value
    let cpuCount = getCPUCount()
    check cpuCount > 0

  test "detectEmbeddedDevice returns boolean":
    ## Test that embedded device detection returns a boolean
    let isEmbedded = detectEmbeddedDevice()
    check isEmbedded in [true, false]

suite "Platform Capabilities":

  test "detectPlatform returns valid capabilities":
    ## Property 14: Platform Detection Accuracy
    ## For any platform, detectPlatform() SHALL return accurate capabilities
    let caps = detectPlatform()

    # Verify OS type is valid
    check caps.osType in [Linux, FreeBSD, OpenBSD, NetBSD, macOS, Embedded]

    # Verify kernel version is non-empty
    check caps.kernelVersion.len >= 0

    # Verify memory and CPU are non-negative
    check caps.memoryTotal >= 0
    check caps.cpuCount > 0

  test "detectPlatform consistency":
    ## Test that multiple calls return consistent results
    let caps1 = detectPlatform()
    let caps2 = detectPlatform()

    check caps1.osType == caps2.osType
    check caps1.isRoot == caps2.isRoot
    check caps1.isEmbedded == caps2.isEmbedded

  test "detectPlatform Linux capabilities":
    ## Test Linux-specific capability detection
    when defined(linux):
      let caps = detectPlatform()
      check caps.osType == Linux
      check caps.hasJails == false
      check caps.hasUnveil == false

  test "detectPlatform FreeBSD capabilities":
    ## Test FreeBSD-specific capability detection
    when defined(freebsd):
      let caps = detectPlatform()
      check caps.osType == FreeBSD
      check caps.hasUserNamespaces == false
      check caps.hasUnveil == false

  test "detectPlatform OpenBSD capabilities":
    ## Test OpenBSD-specific capability detection
    when defined(openbsd):
      let caps = detectPlatform()
      check caps.osType == OpenBSD
      check caps.hasUserNamespaces == false
      check caps.hasJails == false

suite "Isolation Strategy Selection":

  test "selectStrategy returns valid strategy":
    ## Property 15: Strategy Selection Correctness
    ## For any platform capabilities, selectStrategy() SHALL return a valid strategy
    let caps = detectPlatform()
    let strategy = selectStrategy(caps)

    check strategy in [LinuxNamespace, FreeBSDJail, OpenBSDUnveil, POSIXFallback]

  test "selectStrategy Linux with namespaces":
    ## Test strategy selection for Linux with namespace support
    var caps = PlatformCapabilities(
      osType: Linux,
      hasUserNamespaces: true,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "5.10.0",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == LinuxNamespace

  test "selectStrategy Linux without namespaces":
    ## Test strategy selection for Linux without namespace support
    var caps = PlatformCapabilities(
      osType: Linux,
      hasUserNamespaces: false,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "4.4.0",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == POSIXFallback

  test "selectStrategy FreeBSD with jails":
    ## Test strategy selection for FreeBSD with jail support
    var caps = PlatformCapabilities(
      osType: FreeBSD,
      hasUserNamespaces: false,
      hasJails: true,
      hasUnveil: false,
      isRoot: true,
      kernelVersion: "11.0",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == FreeBSDJail

  test "selectStrategy FreeBSD without jails":
    ## Test strategy selection for FreeBSD without jail support
    var caps = PlatformCapabilities(
      osType: FreeBSD,
      hasUserNamespaces: false,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "10.0",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == POSIXFallback

  test "selectStrategy OpenBSD with unveil":
    ## Test strategy selection for OpenBSD with unveil support
    var caps = PlatformCapabilities(
      osType: OpenBSD,
      hasUserNamespaces: false,
      hasJails: false,
      hasUnveil: true,
      isRoot: true,
      kernelVersion: "6.4",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == OpenBSDUnveil

  test "selectStrategy OpenBSD without unveil":
    ## Test strategy selection for OpenBSD without unveil support
    var caps = PlatformCapabilities(
      osType: OpenBSD,
      hasUserNamespaces: false,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "6.3",
      isEmbedded: false,
      memoryTotal: 8 * 1024 * 1024 * 1024,
      cpuCount: 4
    )
    let strategy = selectStrategy(caps)
    check strategy == POSIXFallback

  test "selectStrategy embedded device":
    ## Test strategy selection for embedded device
    var caps = PlatformCapabilities(
      osType: Linux,
      hasUserNamespaces: false,
      hasJails: false,
      hasUnveil: false,
      isRoot: false,
      kernelVersion: "4.14.0",
      isEmbedded: true,
      memoryTotal: 256 * 1024 * 1024,
      cpuCount: 1
    )
    let strategy = selectStrategy(caps)
    check strategy == POSIXFallback

suite "Installation Mode Selection":

  test "selectMode returns valid mode":
    ## Property 16: Mode Selection Logic
    ## For any strategy, selectMode() SHALL return a valid mode
    let caps = detectPlatform()
    let strategy = selectStrategy(caps)
    let mode = selectMode(strategy, none(InstallMode))

    check mode in [UserMode, SystemMode]

  test "selectMode Linux namespace prefers user mode":
    ## Test that Linux namespace strategy prefers user mode
    let strategy = LinuxNamespace
    let mode = selectMode(strategy, none(InstallMode))
    check mode == UserMode

  test "selectMode FreeBSD jail requires system mode":
    ## Test that FreeBSD jail strategy requires system mode
    let strategy = FreeBSDJail
    let mode = selectMode(strategy, none(InstallMode))
    check mode == SystemMode

  test "selectMode OpenBSD unveil requires system mode":
    ## Test that OpenBSD unveil strategy requires system mode
    let strategy = OpenBSDUnveil
    let mode = selectMode(strategy, none(InstallMode))
    check mode == SystemMode

  test "selectMode POSIX fallback as root":
    ## Test POSIX fallback mode selection as root
    let strategy = POSIXFallback
    let mode = selectMode(strategy, none(InstallMode))
    # Mode depends on whether running as root
    check mode in [UserMode, SystemMode]

  test "selectMode respects user request for valid strategy":
    ## Test that user request is respected when valid
    let strategy = LinuxNamespace
    let mode = selectMode(strategy, some(UserMode))
    check mode == UserMode

  test "selectMode falls back on invalid user request":
    ## Test that invalid user request falls back gracefully
    let strategy = FreeBSDJail
    let mode = selectMode(strategy, some(UserMode))
    # Should fall back to SystemMode since FreeBSD jail doesn't support user mode
    check mode == SystemMode

suite "Strategy Information":

  test "getStrategyDescription returns non-empty string":
    ## Test that strategy descriptions are available
    for strategy in [LinuxNamespace, FreeBSDJail, OpenBSDUnveil, POSIXFallback]:
      let desc = getStrategyDescription(strategy)
      check desc.len > 0

  test "getSecurityLevel returns valid level":
    ## Test that security levels are valid (1-5)
    for strategy in [LinuxNamespace, FreeBSDJail, OpenBSDUnveil, POSIXFallback]:
      let level = getSecurityLevel(strategy)
      check level >= 1 and level <= 5

  test "getStrategyInfo returns non-empty string":
    ## Test that strategy info is available
    for strategy in [LinuxNamespace, FreeBSDJail, OpenBSDUnveil, POSIXFallback]:
      let info = getStrategyInfo(strategy)
      check info.len > 0

  test "security levels are reasonable":
    ## Test that security levels make sense
    check getSecurityLevel(LinuxNamespace) == 5
    check getSecurityLevel(FreeBSDJail) == 5
    check getSecurityLevel(OpenBSDUnveil) == 4
    check getSecurityLevel(POSIXFallback) == 1

suite "Embedded Device Constraints":

  test "getEmbeddedConstraints returns valid constraints":
    ## Test that embedded constraints are valid
    let constraints = getEmbeddedConstraints()

    check constraints.maxConcurrentDownloads > 0
    check constraints.maxConcurrentBuilds > 0
    check constraints.maxCacheSize > 0

  test "embedded constraints are reasonable":
    ## Test that embedded constraints are reasonable
    let constraints = getEmbeddedConstraints()

    # Downloads should be limited
    check constraints.maxConcurrentDownloads <= 4

    # Builds should be single-threaded
    check constraints.maxConcurrentBuilds == 1

    # Cache should be limited
    check constraints.maxCacheSize <= 1024 * 1024 * 1024  # Max 1GB

  test "embedded constraints enable compression":
    ## Test that compression is enabled for embedded
    let constraints = getEmbeddedConstraints()
    check constraints.enableCompression == true

  test "embedded constraints enable deduplication":
    ## Test that deduplication is enabled for embedded
    let constraints = getEmbeddedConstraints()
    check constraints.enableDeduplication == true

suite "Byte Formatting":

  test "formatBytes handles bytes":
    ## Test byte formatting for small sizes
    check formatBytes(512) == "512B"

  test "formatBytes handles kilobytes":
    ## Test byte formatting for kilobytes
    check formatBytes(1024) == "1KB"
    check formatBytes(2048) == "2KB"

  test "formatBytes handles megabytes":
    ## Test byte formatting for megabytes
    check formatBytes(1024 * 1024) == "1MB"
    check formatBytes(512 * 1024 * 1024) == "512MB"

  test "formatBytes handles gigabytes":
    ## Test byte formatting for gigabytes
    check formatBytes(1024 * 1024 * 1024) == "1GB"
    check formatBytes(8 * 1024 * 1024 * 1024) == "8GB"

suite "Platform Summary":

  test "printPlatformInfo does not crash":
    ## Test that platform info printing works
    let caps = detectPlatform()
    # This should not crash or raise an exception
    printPlatformInfo(caps)

  test "printEmbeddedConstraints does not crash":
    ## Test that embedded constraints printing works
    let constraints = getEmbeddedConstraints()
    # This should not crash or raise an exception
    printEmbeddedConstraints(constraints)
