## test_pkgsrc_adapter.nim
## Unit tests for PkgsrcAdapter

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

suite "PkgsrcAdapter Tests":

  test "PkgsrcAdapter initialization":
    let adapter = newPkgsrcAdapter()

    check adapter != nil
    check adapter.name == "pkgsrc"
    check adapter.pkgsrcRoot == "/usr/pkgsrc"
    check adapter.makeConf == "/etc/mk.conf"
    check adapter.packageCount == 27000

  test "PkgsrcAdapter availability check":
    let adapter = newPkgsrcAdapter()
    let available = adapter.isAvailable()

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

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

    let validNames = @[
      "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 during name validation
      let result = adapter.buildPackage(request)
      # May fail for other reasons, but not validation
      if not result.success and result.errors.len > 0:
        check "Invalid package name" notin result.errors[0]

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

    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 "mk.conf generation - no options":
    let adapter = newPkgsrcAdapter()

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

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

    # Check that mk.conf file was created
    let mkConfFile = getTempDir() / "nip-test-cache" / "pkgsrc" / "mk.conf.test-package"
    if fileExists(mkConfFile):
      let content = readFile(mkConfFile)
      check "# Generated by NIP" in content

  test "mk.conf generation - with options":
    let adapter = newPkgsrcAdapter()

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

    discard adapter.buildPackage(request)

    let mkConfFile = getTempDir() / "nip-test-cache" / "pkgsrc" / "mk.conf.firefox"
    if fileExists(mkConfFile):
      let content = readFile(mkConfFile)
      check "# Generated by NIP" in content
      check "PKG_OPTIONS.firefox=" in content
      check "wayland" in content
      check "pulseaudio" in content

  test "PKG_OPTIONS validation - valid options":
    let adapter = newPkgsrcAdapter()

    let validOptions = @[
      "wayland",
      "pulseaudio",
      "enable-feature",
      "with_option",
      "flag123"
    ]

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

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

  test "PKG_OPTIONS validation - invalid options":
    let adapter = newPkgsrcAdapter()

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

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

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

    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 == "pkgsrc"
    check result.packageName == "test-package"
    # success may be false if package not found, which is expected

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

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

    let adapter = newPkgsrcAdapter()
    let request = BuildRequest(
      packageName: "test",
      version: "",
      variantFlags: initTable[string, seq[string]](),
      sourceFlags: @["option1", "option2"],  # Add options to trigger mk.conf generation
      cacheDir: cacheDir,
      verbose: false
    )

    discard adapter.buildPackage(request)

    # Check that cache directory was created (even if build fails)
    # The mk.conf generation should create the directory
    check dirExists(cacheDir / "pkgsrc") or not dirExists("/usr/pkgsrc")

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

    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 - package not found":
    let adapter = newPkgsrcAdapter()

    # Use a package name that will definitely not be found
    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
    check "not found" in result.errors[0].toLower()

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

    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 PKGSRC is actually available
if dirExists("/usr/pkgsrc"):
  suite "PkgsrcAdapter Search Tests (PKGSRC Available)":

    test "Search for package in common category":
      let adapter = newPkgsrcAdapter()

      # Try to search for a common package
      # Note: This test is system-dependent
      let result = adapter.searchPackage("bash")

      # May or may not find it depending on PKGSRC installation
      if result.isSome:
        let info = result.get()
        check info.name == "bash"
        check info.source == "pkgsrc"
        check info.available == true

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

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

      # Should return none
      check result.isNone

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

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

      # Should return none due to validation
      check result.isNone

    test "Extract package information from Makefile":
      let adapter = newPkgsrcAdapter()

      # Try a common package
      let result = adapter.searchPackage("bash")

      if result.isSome:
        let info = result.get()
        # Should have extracted some information
        check info.version != ""
        check info.category != ""
