## Property-Based Tests for NEXTER Manifest - TDD Approach
##
## **Feature:** 01-nip-unified-storage-and-formats
## **Property 3:** Manifest Roundtrip
## **Validates:** Requirements 6.4
##
## **Property Statement:**
## For any NEXTER manifest, parsing and regenerating SHALL produce semantically equivalent KDL
##
## **TDD Strategy:**
## Thistten FIRST to expose gaps in the implementation.
## Test failures will drive the implementation of parseNEXTERManifest and generateNEXTERManifest.
## We use a MAXIMALLY POPULATED manifest to ensure ALL fields are tested.

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

# ============================================================================
# Helper: Create Maximally Populated NEXTER Manifest
# ============================================================================

proc createFullNEXTERManifest*(): NEXTERManifest =
  ## Creates a maximally populated NEXTERManifest instance for roundtrip testing.
  ## This ensures we test ALL fields, not just the minimal required ones.
  ##
  ## **Philosophy:** Test the hardest case first. If this passes, simpler cases will too.

  result = NEXTERManifest(
    name: "dev-environment",
    version: parseSemanticVersion("2.1.0-beta.3+build.456"),
    buildDate: parse("2025-11-20T15:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),

    metadata: ContainerInfo(
      description: "Development environment container with full toolchain.",
      license: "MIT",
      homepage: some("https://nexus.os/containers/dev-env"),
      author: some("DevOps Team"),
      maintainer: some("devops@nexus.os"),
      purpose: some("development"),
      tags: @["development", "toolchain", "isolated"]
    ),

    provenance: ProvenanceInfo(
      source: "https://nexus.os/sources/dev-env-2.1.0.tar.gz",
      sourceHash: "xxh3-container-source-hash-abc",
      upstream: some("https://github.com/nexusos/dev-containers"),
      buildTimestamp: parse("2025-11-20T15:30:00Z", "yyyy-MM-dd'T'HH:mm:ss'Z'"),
      builder: some("ci-bot@nexus.os")
    ),

    buildConfig: BuildConfiguration(
      configureFlags: @["--enable-dev-tools", "--with-debugger"],
      compilerFlags: @["-O2", "-g", "-march=x86-64"],
      compilerVersion: "gcc-13.2.0",
      targetArchitecture: "x86_64",
      libc: "musl",
      allocator: "jemalloc",
      buildSystem: "custom"
    ),

    base: BaseConfig(
      baseImage: some("alpine"),
      baseVersion: some("3.18"),
      packages: @["bash", "git", "vim", "gcc", "make"]
    ),

    environment: {
      "PATH": "/usr/local/bin:/usr/bin:/bin",
      "HOME": "/home/developer",
      "LANG": "en_US.UTF-8",
      "TERM": "xterm-256color"
    }.toTable(),

    casChunks: @[
      ChunkReference(
        hash: "xxh3-chunk1-base",
        size: 52428800,  # 50MB
        chunkType: Base,
        path: "base/alpine-3.18.tar"
      ),
      ChunkReference(
        hash: "xxh3-chunk2-tools",
        size: 10485760,  # 10MB
        chunkType: Tools,
        path: "tools/dev-tools.tar"
      ),
      ChunkReference(
        hash: "xxh3-chunk3-config",
        size: 8192,
        chunkType: Config,
        path: "config/container.conf"
      )
    ],

    namespace: ContainerNamespace(
      isolationType: "full",
      capabilities: @["CAP_NET_ADMIN", "CAP_SYS_PTRACE"],
      mounts: @[
        MountSpec(
          source: "/home/user/projects",
          target: "/workspace",
          mountType: "bind",
          readOnly: false,
          options: @["rw", "rbind"]
        ),
        MountSpec(
          source: "tmpfs",
          target: "/tmp",
          mountType: "tmpfs",
          readOnly: false,
          options: @["size=1G"]
        )
      ],
      devices: @[
        DeviceSpec(
          path: "/dev/null",
          deviceType: "c",
          major: 1,
          minor: 3,
          permissions: "rwm"
        ),
        DeviceSpec(
          path: "/dev/zero",
          deviceType: "c",
          major: 1,
          minor: 5,
          permissions: "rwm"
        )
      ]
    ),

    startup: StartupConfig(
      command: @["/bin/bash", "-l"],
      workingDir: "/workspace",
      user: some("developer"),
      entrypoint: some("/usr/local/bin/container-init.sh")
    ),

    buildHash: "xxh3-container-build-hash",

    signature: SignatureInfo(
      algorithm: "ed25519",
      keyId: "container-builder-2024",
      signature: "base64-encoded-container-signature"
    )
  )

# ============================================================================
# Property-Based Tests
# ============================================================================

suite "NEXTER Manifest Roundtrip Property Tests (Task 8.1)":

  test "Property 3: KDL Generation - Verify output structure":
    ## **Phase 1:** Test KDL generation in isolation
    ## This verifies generateNEXTERManifest works before testing roundtrip

    let manifest = createFullNEXTERManifest()
    let kdlString = generateNEXTERManifest(manifest)

    echo "\n=== Generated KDL (Phase 1) ===\n"
    echo kdlString
    echo "=== End KDL ===\n"

    # Verify KDL structure
    check kdlString.contains("container \"dev-environment\"")
    check kdlString.contains("version \"2.1.0-beta.3+build.456\"")
    check kdlString.contains("metadata {")
    check kdlString.contains("provenance {")
    check kdlString.contains("build_config {")
    check kdlString.contains("base {")
    check kdlString.contains("environment {")
    check kdlString.contains("cas_chunks {")
    check kdlString.contains("namespace {")
    check kdlString.contains("startup {")
    check kdlString.contains("build_hash \"xxh3-container-build-hash\"")
    check kdlString.contains("signature {")

  test "Property 3: Full Roundtrip - ALL FIELDS":
    ## **Phase 2:** Full roundtrip test
    ## This is the MAIN test that will expose all gaps
    ##
    ## **Feature: 01-nip-unified-storage-and-formats, Property 3: Manifest Roundtrip**
    ## **Validates: Requirements 6.4**

    let originalManifest = createFullNEXTERManifest()

    # Step 1: Generate KDL
    let kdlString = generateNEXTERManifest(originalManifest)
    check kdlString.len > 0

    # Step 2: Parse KDL back
    let parsedManifest = parseNEXTERManifest(kdlString)

    # Step 3: Verify ALL fields preserved
    if parsedManifest.name != originalManifest.name:
      echo "Name mismatch: ", parsedManifest.name, " != ", originalManifest.name
    check parsedManifest.name == originalManifest.name

    if parsedManifest.version != originalManifest.version:
      echo "Version mismatch:"
      echo "  Parsed: ", parsedManifest.version
      echo "  Original: ", originalManifest.version
    check parsedManifest.version == originalManifest.version

    if parsedManifest.buildHash != originalManifest.buildHash:
      echo "BuildHash mismatch: ", parsedManifest.buildHash, " != ", originalManifest.buildHash
    check parsedManifest.buildHash == originalManifest.buildHash

    # Step 4: Verify deterministic generation
    let kdlString2 = generateNEXTERManifest(parsedManifest)
    if kdlString != kdlString2:
      echo "KDL strings don't match!"
      echo "=== Original KDL ==="
      echo kdlString
      echo "=== Regenerated KDL ==="
      echo kdlString2
    check kdlString == kdlString2

suite "NEXTER Manifest Validation Tests":

  test "Validate manifest with valid xxh3 hashes":
    let validManifest = NEXTERManifest(
      name: "valid-container",
      version: parseSemanticVersion("1.0.0"),
      buildDate: now(),
      metadata: ContainerInfo(
        description: "Valid container",
        license: "MIT"
      ),
      provenance: ProvenanceInfo(
        source: "https://example.com/valid.tar.gz",
        sourceHash: "xxh3-valid123",
        buildTimestamp: now()
      ),
      buildConfig: BuildConfiguration(
        compilerVersion: "gcc-13.2.0",
        targetArchitecture: "x86_64",
        libc: "musl",
        allocator: "default",
        buildSystem: "custom"
      ),
      base: BaseConfig(
        baseImage: some("alpine"),
        baseVersion: some("3.18"),
        packages: @[]
      ),
      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-build123",
      signature: SignatureInfo(
        algorithm: "ed25519",
        keyId: "test-key",
        signature: "test-signature"
      )
    )

    let issues = validateNEXTERManifest(validManifest)
    check issues.len == 0

  test "Property 3: Determinism - Same input produces same output":
    ## **Feature: 01-nip-unified-storage-and-formats, Property 9: Manifest Hash Determinism**
    ## **Validates: Requirements 6.4, 7.5**
    ##
    ## This test verifies that generateNEXTERManifest is deterministic

    let manifest = createFullNEXTERManifest()

    # Generate KDL multiple times
    let kdl1 = generateNEXTERManifest(manifest)
    let kdl2 = generateNEXTERManifest(manifest)
    let kdl3 = generateNEXTERManifest(manifest)

    # All outputs should be identical
    check kdl1 == kdl2
    check kdl2 == kdl3
    check kdl1 == kdl3
