## Tests for NPK Package Format Handler
##
## This module tests the NPK package creation, validation, and conversion
## functionality to ensure proper package format handling.

import unittest, os, times, options
import ../src/nimpak/types
import ../src/nimpak/packages
import ../src/nimpak/cas


suite "NPK Package Format Handler":

  setup:
    let testDir = getTempDir() / "nimpak_test_packages"
    let sourceDir = testDir / "source"
    let casDir = testDir / "cas"

    # Clean up any existing test directory
    if dirExists(testDir):
      removeDir(testDir)

    # Create test directory structure
    createDir(sourceDir)
    createDir(sourceDir / "bin")
    createDir(sourceDir / "share" / "man")

    # Create test files
    writeFile(sourceDir / "bin" / "testapp", "#!/bin/bash\necho 'Hello World'\n")
    writeFile(sourceDir / "share" / "man" / "testapp.1", ".TH TESTAPP 1\n.SH NAME\ntestapp - test application\n")
    writeFile(sourceDir / "README.md", "# Test Application\nThis is a test application.\n")

    # Initialize CAS manager
    let casManager = initCasManager(casDir, casDir / "system")

  teardown:
    let testDir = getTempDir() / "nimpak_test_packages"
    if dirExists(testDir):
      removeDir(testDir)

  test "create NPK package from Fragment":
    let fragment = Fragment(
      id: PackageId(
        name: "testapp",
        version: "1.0.0",
        stream: Stable
      ),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application for NPK testing",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @["test", "cli"],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @["test"]
        )
      ),
      acul: AculCompliance(
        required: false,
        membership: "",
        attribution: "",
        buildLog: ""
      )
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let result = createNpkPackage(fragment, sourceDir)

    check result.isOk
    let npk = result.get()
    check npk.metadata.id.name == "testapp"
    check npk.metadata.id.version == "1.0.0"
    check npk.files.len == 3  # bin/testapp, share/man/testapp.1, README.md
    check npk.manifest.totalSize > 0
    check npk.manifest.merkleRoot.len > 0
    check npk.signature.isNone  # No signature by default

  test "validate NPK package":
    let fragment = Fragment(
      id: PackageId(name: "testapp", version: "1.0.0", stream: Stable),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @[],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @[]
        )
      ),
      acul: AculCompliance(required: false, membership: "", attribution:, buildLog: "")
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let createResult = createNpkPackage(fragment, sourceDir)
    check createResult.isOk

    let npk = createResult.get()
    let validation = validateNpkPackage(npk)

    check validation.valid == true
    check validation.errors.len == 0

  test "validate invalid NPK package":
    # Create invalid package with empty name
    let invalidFragment = Fragment(
      id: PackageId(name: "", version: "", stream: Stable),  # Invalid: empty name and version
      source: Source(
        url: "",  # Invalid: empty URL
        hash: "",  # Invalid: empty hash
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Invalid test package",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @[],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @[]
        )
      ),
      acul: AculCompliance(required: false, membership: "", attribution: "", buildLog: "")
    )

    let npk = NpkPackage(
      metadata: invalidFragment,
      files: @[],  # No files
      manifest: PackageManifest(
        files: @[],
        totalSize: 0,
        created: now(),
        merkleRoot: ""  # Invalid: empty merkle root
      ),
      signature: none(Signature)
    )

    let validation = validateNpkPackage(npk)

    check validation.valid == false
    check validation.errors.len > 0

    # Check specific validation errors
    let errorFields = validation.errors.mapIt(it.field)
    check "metadata.id.name" in errorFields
    check "metadata.id.version" in errorFields
    check "metadata.source.url" in errorFields
    check "metadata.source.hash" in errorFields
    check "manifest.merkleRoot" in errorFields

  test "serialize NPK to KDL format":
    let fragment = Fragment(
      id: PackageId(name: "testapp", version: "1.0.0", stream: Stable),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[
        PackageId(name: "libtest", version: "2.0.0", stream: Stable)
      ],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @["test", "cli"],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @["test"]
        )
      ),
      acul: AculCompliance(
        required: true,
        membership: "NexusOS-Community",
        attribution: "Original: testapp, Packaged: NexusOS",
        buildLog: ""
      )
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let createResult = createNpkPackage(fragment, sourceDir)
    check createResult.isOk

    let npk = createResult.get()
    let kdlContent = serializeToKdl(npk)

    # Check KDL content contains expected elements
    check "package \"testapp\"" in kdlContent
    check "version \"1.0.0\"" in kdlContent
    check "stream \"Stable\"" in kdlContent
    check "method \"Http\"" in kdlContent
    check "libc \"Musl\"" in kdlContent
    check "libtest \"2.0.0\"" in kdlContent
    check "required true" in kdlContent
    check "membership \"NexusOS-Community\"" in kdlContent

  test "sign and verify NPK package":
    let fragment = Fragment(
      id: PackageId(name: "testapp", version: "1.0.0", stream: Stable),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @[],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @[]
        )
      ),
      acul: AculCompliance(required: false, membership: "", attribution: "", buildLog: "")
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let createResult = createNpkPackage(fragment, sourceDir)
    check createResult.isOk

    var npk = createResult.get()
    check npk.signature.isNone

    # Sign the package
    let privateKey = @[0x01'u8, 0x02'u8, 0x03'u8]  # Placeholder key
    let signResult = signNpkPackage(npk, "test-key-id", privateKey)
    check signResult.isOk
    check npk.signature.isSome

    let signature = npk.signature.get()
    check signature.keyId == "test-key-id"
    check signature.algorithm == "ed25519"

    # Verify the signature
    let publicKey = @[0x04'u8, 0x05'u8, 0x06'u8]  # Placeholder key
    let verifyResult = verifyNpkSignature(npk, publicKey)
    check verifyResult.isOk
    # Note: This will be true with placeholder implementation

  test "create and load NPK archive":
    let fragment = Fragment(
      id: PackageId(name: "testapp", version: "1.0.0", stream: Stable),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @[],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @[]
        )
      ),
      acul: AculCompliance(required: false, membership: "", attribution: "", buildLog: "")
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let createResult = createNpkPackage(fragment, sourceDir)
    check createResult.isOk

    let npk = createResult.get()
    let archivePath = getTempDir() / "nimpak_test_packages" / "testapp-1.0.0.npk"

    # Create archive
    let archiveResult = createNpkArchive(npk, archivePath)
    check archiveResult.isOk
    check dirExists(archivePath)  # Currently creates directory, will be tar.gz later
    check fileExists(archivePath / "package.kdl")
    check fileExists(archivePath / "manifest.json")

    # Load archive (will fail with current placeholder implementation)
    let loadResult = loadNpkArchive(archivePath)
    check loadResult.isErr  # Expected to fail until KDL parsing is implemented

  test "get NPK package info":
    let fragment = Fragment(
      id: PackageId(name: "testapp", version: "1.0.0", stream: Stable),
      source: Source(
        url: "https://example.com/testapp-1.0.0.tar.gz",
        hash: "blake2b-abc123def456",
        hashAlgorithm: "blake2b",
        sourceMethod: Http,
        timestamp: now()
      ),
      dependencies: @[],
      buildSystem: Custom,
      metadata: PackageMetadata(
        description: "Test application",
        license: "MIT",
        maintainer: "test@example.com",
        tags: @[],
        runtime: RuntimeProfile(
          libc: Musl,
          allocator: System,
          systemdAware: false,
          reproducible: true,
          tags: @[]
        )
      ),
      acul: AculCompliance(required: false, membership: "", attribution: "", buildLog: "")
    )

    let sourceDir = getTempDir() / "nimpak_test_packages" / "source"
    let createResult = createNpkPackage(fragment, sourceDir)
    check createResult.isOk

    let npk = createResult.get()
    let info = getNpkInfo(npk)

    check "NPK Package: testapp v1.0.0" in info
    check "Stream: Stable" in info
    check "Files: 3" in info
    check "Signed: No" in info
