## test_nix_adapter.nim
## Unit tests for NixAdapter

import std/[unittest, tables, os, strutils, options]
import ../src/nimpak/build/[types, nix_adapter]

suite "NixAdapter Tests":

  test "NixAdapter initialization":
    let adapter = newNixAdapter()

    check adapter != nil
    check adapter.name == "nix"
    check adapter.nixpkgsPath == "<nixpkgs>"
    check adapter.storeDir == "/nix/store"
    check adapter.packageCount == 100000

  test "NixAdapter availability check":
    let adapter = newNixAdapter()
    let available = adapter.isAvailable()

    # Should match actual system state
    check available == dirExists("/nix")

  test "Package name validation - valid names":
    let adapter = newNixAdapter()

    let validNames = @[
      "firefox",
      "nixpkgs.firefox",
      "my-package",
      "package_name",
      "package.with.dots",
      "Package123"
    ]

    for name in validNames:
      let request = BuildRequest(
        packageName: name,
        version: "",
        variantFlags: initTable[string, seq[string]](),
        sourceFlags: @[],
        cacheDir: getTempDir(),
        verbose: false
      )

      # Should not raise ValidationError
      try:
        discard adapter.buildPackage(request)
      except ValidationError:
        fail()

  test "Package name validation - invalid names":
    let adapter = newNixAdapter()

    let invalidNames = @[
      "",
      "../etc/passwd",
      "/absolute/path",
      "package;rm -rf /",
      "package`whoami`",
      "package$(whoami)",
      "a" & "b".repeat(300)  # Too long
    ]

    for name in invalidNames:
      let request = BuildRequest(
        packageName: name,
        version: "",
        variantFlags: initTable[string, seq[string]](),
        sourceFlags: @[],
        cacheDir: getTempDir(),
        verbose: false
      )

      let result = adapter.buildPackage(request)
      check result.success == false
      check result.errors.len > 0

  test "Nix expression generation - no overrides":
    let adapter = newNixAdapter()

    # We can't directly test the private generateNixExpression,
    # but we can test it through buildPackage
    let request = BuildRequest(
      packageName: "hello",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    # This will generate the expression file
    discard adapter.buildPackage(request)

    # Check that expression file was created
    let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-hello.nix"
    if fileExists(exprFile):
      let content = readFile(exprFile)
      check "with import <nixpkgs> {};" in content
      check "hello" in content

  test "Nix expression generation - with overrides":
    let adapter = newNixAdapter()

    let request = BuildRequest(
      packageName: "firefox",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @["waylandSupport = true", "enableLTO = true"],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    discard adapter.buildPackage(request)

    let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-firefox.nix"
    if fileExists(exprFile):
      let content = readFile(exprFile)
      check "with import <nixpkgs> {};" in content
      check "firefox" in content
      check ".override" in content
      check "waylandSupport = true" in content
      check "enableLTO = true" in content

  test "Override key validation - valid keys":
    let adapter = newNixAdapter()

    let validFlags = @[
      "waylandSupport = true",
      "enable-feature = false",
      "with_option = true",
      "flag123 = true"
    ]

    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: validFlags,
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    # Should not raise ValidationError
    let result = adapter.buildPackage(request)
    # May fail at nix-build stage, but not at validation
    if not result.success and result.errors.len > 0:
      check "Invalid override key" notin result.errors[0]

  test "Override key validation - invalid keys":
    let adapter = newNixAdapter()

    # Test with malicious override key
    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @["bad;key = true"],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    let result = adapter.buildPackage(request)
    check result.success == false

  test "Build result structure":
    let adapter = newNixAdapter()

    let request = BuildRequest(
      packageName: "test-package",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    let result = adapter.buildPackage(request)

    # Check result structure
    check result.source == "nix"
    check result.packageName == "test-package"
    # success may be false if nix-build fails, which is expected in test environment

  test "Cache directory creation":
    let cacheDir = getTempDir() / "nip-test-cache-new"

    # Remove if exists
    if dirExists(cacheDir):
      removeDir(cacheDir)

    let adapter = newNixAdapter()
    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: cacheDir,
      verbose: false
    )

    discard adapter.buildPackage(request)

    # Check that cache directory was created
    check dirExists(cacheDir / "nix")

  test "Package name sanitization for filenames":
    let adapter = newNixAdapter()

    # Package name with dots should be sanitized for filename
    let request = BuildRequest(
      packageName: "nixpkgs.firefox",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    discard adapter.buildPackage(request)

    # Check that file was created with sanitized name
    let exprFile = getTempDir() / "nip-test-cache" / "nix" / "build-nixpkgs_firefox.nix"
    check fileExists(exprFile)

  test "Variant flags in build result":
    let adapter = newNixAdapter()

    var variantFlags = initTable[string, seq[string]]()
    variantFlags["graphics"] = @["wayland", "vulkan"]
    variantFlags["audio"] = @["pipewire"]

    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: variantFlags,
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    let result = adapter.buildPackage(request)

    # Variant flags should be preserved in result
    check result.variantDomains.hasKey("graphics")
    check result.variantDomains.hasKey("audio")

  test "Error handling - build failure":
    let adapter = newNixAdapter()

    # Use a package name that will definitely fail
    let request = BuildRequest(
      packageName: "this-package-definitely-does-not-exist-12345",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: false
    )

    let result = adapter.buildPackage(request)

    # Should fail gracefully
    check result.success == false
    check result.errors.len > 0

  test "Verbose mode":
    let adapter = newNixAdapter()

    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @[],
      cacheDir: getTempDir() / "nip-test-cache",
      verbose: true
    )

    # Should not crash with verbose mode
    discard adapter.buildPackage(request)

# Only run search tests if Nix is actually available
if dirExists("/nix"):
  suite "NixAdapter Search Tests (Nix Available)":

    test "Search for existing package":
      let adapter = newNixAdapter()

      # Search for a package that should exist
      let result = adapter.searchPackage("hello")

      # May or may not find it depending on nixpkgs availability
      if result.isSome:
        let info = result.get()
        check info.name == "hello"
        check info.source == "nix"
        check info.available == true

    test "Search for non-existent package":
      let adapter = newNixAdapter()

      let result = adapter.searchPackage("this-package-does-not-exist-xyz123")

      # Should return none
      check result.isNone

    test "Search with invalid package name":
      let adapter = newNixAdapter()

      let result = adapter.searchPackage("../etc/passwd")

      # Should return none due to validation
      check result.isNone
