# tests/test_npk_conversion.nim
# Unit tests for NPK conversion with build hash integration

import unittest, os, times, tables
import ../src/nimpak/npk_conversion
import ../src/nimpak/grafting
import ../src/nip/types

suite "NPK Conversion Tests":

  setup:
    let tempDir = getTempDir() / "nimpak_test_npk"
    if dirExists(tempDir):
      removeDir(tempDir)
    createDir(tempDir)

  teardown:
    let tempDir = getTempDir() / "nimpak_test_npk"
    if dirExists(tempDir):
      removeDir(tempDir)

  test "NPK converter creation":
    let conv = newNPKConverter()
    check conv.compressionLevel == 6
    check conv.includeProvenance == true
    check conv.calculateBuildHash == true
    check conv.signPackages == false

  test "NPK converter with custom settings":
    var conv = newNPKConverter("/custom/output")
    conv.compressionLevel = 9
    conv.signPackages = true
    conv.keyPath = "/path/to/key"

    check conv.outputDir == "/custom/output"
    check conv.compressionLevel == 9
    check conv.signPackages == true
    check conv.keyPath == "/path/to/key"

  test "Build configuration creation":
    let metadata = GraftedPackageMetadata(
      packageName: "test-package",
      version: "1.0.0",
      source: "test",
      graftedAt: now(),
      originalHash: "test-hash",
      graftHash: "graft-hash",
      buildLog: "test build log",
      provenance: ProvenanceInfo()
    )

    let buildConfig = extractBuildConfiguration(metadata)
    check buildConfig.sourceHash == "test-hash"
    check buildConfig.targetArchitecture == "x86_64"
    check buildConfig.libc == "musl"

  test "Build hash calculation":
    let config = BuildConfiguration(
      sourceHash: "source-123",
      sourceTimestamp: now(),
      configureFlags: @["--enable-feature"],
      compilerFlags: @["-O2"],
      linkerFlags: @[],
      compilerVersion: "gcc-11.0",
      nimVersion: "2.0.0",
      nimFlags: @[],
      targetArchitecture: "x86_64",
      libc: "musl",
      libcVersion: "1.2.4",
      allocator: "default",
      allocatorVersion: "system",
      environmentVars: initTable[string, string](),
      dependencies: @[]
    )

    let hashResult = calculateBuildHash(config)
    check hashResult.isOk

    let buildHash = hashResult.get()
    check buildHash.algorithm == "blake3"
    check buildHash.hash.startsWith("blake3-")
    check buildHash.components.len > 0

  test "File scanning":
    let testDir = getTempDir() / "test_scan"
    createDir(testDir)

    # Create test files
    writeFile(testDir / "test1.txt", "content1")
    writeFile(testDir / "test2.txt", "content2")
    createDir(testDir / "subdir")
    writeFile(testDir / "subdir" / "test3.txt", "content3")

    let filesResult = scanPackageFiles(testDir)
    check filesResult.isOk

    let files = filesResult.get()
    check files.len == 3

    # Check file paths are normalized
    let paths = files.mapIt(it.path)
    check "/test1.txt" in paths
    check "/test2.txt" in paths
    check "/subdir/test3.txt" in paths

    # Cleanup
    removeDir(testDir)

  test "Manifest generation":
    let tempDir = getTempDir() / "test_manifest"
    createDir(tempDir)
    writeFile(tempDir / "test.txt", "test content")

    let metadata = GraftedPackageMetadata(
      packageName: "test-package",
      version: "1.0.0",
      source: "test",
      graftedAt: now(),
      originalHash: "original-hash",
      graftHash: "graft-hash",
      buildLog: "test build log",
      provenance: ProvenanceInfo(
        originalSource: "test-source",
        downloadUrl: "https://example.com/package",
        archivePath: "/path/to/archive",
        extractedPath: tempDir,
        conversionLog: "test conversion"
      )
    )

    let conv = newNPKConverter()
    let manifestResult = generateNPKManifest(conv, metadata, tempDir)
    check manifestResult.isOk

    let manifest = manifestResult.get()
    check manifest.name == "test-package"
    check manifest.version == "1.0.0"
    check manifest.sourceHash == "original-hash"
    check manifest.files.len == 1
    check manifest.files[0].path == "/test.txt"

    removeDir(tempDir)

  test "KDL manifest writing":
    let manifest = NPKManifest(
      name: "test-package",
      version: "1.0.0",
      description: "Test package",
      homepage: "https://example.com",
      license: @["MIT"],
      maintainer: "Test Maintainer",
      buildHash: "build-hash-123",
      sourceHash: "source-hash-123",
      artifactHash: "artifact-hash-123",
      buildConfig: BuildConfiguration(),
      dependencies: @[],
      acul: AculCompliance(
        required: false,
        membership: "Test",
        attribution: "Test attribution",
        buildLog: "test log"
      ),
      files: @[
        NPKFile(
          path: "/test.txt",
          hash: "file-hash-123",
          permissions: "644",
          size: 100
        )
      ],
      provenance: ProvenanceInfo(
        originalSource: "test",
        downloadUrl: "https://example.com",
        archivePath: "/path",
        extractedPath: "/extracted",
        conversionLog: "test"
      ),
      created: now(),
      converterName: "nimpak-test"
    )

    let outputPath = getTempDir() / "test-manifest.kdl"
    let writeResult = writeManifestKDL(manifest, outputPath)
    check writeResult.isOk

    check fileExists(outputPath)
    let content = readFile(outputPath)
    check "package \"test-package\"" in content
    check "version \"1.0.0\"" in content
    check "build_hash \"build-hash-123\"" in content

    removeFile(outputPath)

  test "String array formatting":
    check formatStringArray(@[]) == "\"\""
    check formatStringArray(@["single"]) == "\"single\""
    check formatStringArray(@["first", "second"]) == "\"first\" \"second\""
    check formatStringArray(@["a", "b", "c"]) == "\"a\" \"b\" \"c\""

suite "NPK Integration Tests":

  test "Full conversion workflow":
    let tempDir = getTempDir() / "test_conversion"
    createDir(tempDir)

    # Create test package structure
    let extractedDir = tempDir / "extracted"
    createDir(extractedDir)
    writeFile(extractedDir / "binary", "#!/bin/bash\necho hello")
    writeFile(extractedDir / "config.txt", "setting=value")

    let metadata = GraftedPackageMetadata(
      packageName: "test-conversion",
      version: "2.0.0",
      source: "test",
      graftedAt: now(),
      originalHash: "original-123",
      graftHash: "graft-123",
      buildLog: "successful build",
      provenance: ProvenanceInfo(
        originalSource: "test-conversion",
        downloadUrl: "https://example.com/test",
        archivePath: "/archive/path",
        extractedPath: extractedDir,
        conversionLog: "extracted successfully"
      )
    )

    let conv = newNPKConverter(tempDir / "output")
    let conversionResult = convertToNPK(conv, metadata, extractedDir)

    check conversionResult.isOk
    let result = conversionResult.get()
    check result.success == true
    check result.npkPath.endsWith(".npk")
    check result.buildHash.algorithm == "blake3"
    check result.manifest.name == "test-conversion"

    # Verify NPK file was created
    check fileExists(result.npkPath)

    removeDir(tempDir)