## test_variant_coexistence.nim
## Tests for variant coexistence guarantees (Task 14)

import std/[unittest, tables, os, strutils]
import ../src/nimpak/variant_types
import ../src/nimpak/variant_manager
import ../src/nimpak/variant_database
import ../src/nimpak/config

suite "Variant Coexistence and Conflict Detection":

  setup:
    let testDbPath = getTempDir() / "nip-test-coexistence"
    if dirExists(testDbPath):
      removeDir(testDbPath)
    createDir(testDbPath)

    let vm = newVariantManager(testDbPath)
    let compilerFlags = CompilerFlags(
      cflags: "-O2",
      cxxflags: "-O2",
      ldflags: "",
      makeflags: ""
    )

  teardown:
    if dirExists(testDbPath):
      removeDir(testDbPath)

  test "Conflict detection - no conflict for new variant":
    let domains = {
      "init": @["dinit"],
      "graphics": @["wayland"]
    }.toTable

    let result = vm.createVariant(
      "firefox",
      "118.0",
      domains,
      compilerFlags
    )

    check result.success == true
    check result.reusedExisting == false

  test "Conflict detection - reuse existing variant with same fingerprint":
    let domains = {
      "init": @["dinit"],
      "graphics": @["wayland"]
    }.toTable

    # Create first variant
    let result1 = vm.createVariant(
      "firefox",
      "118.0",
      domains,
      compilerFlags
    )
    check result1.success == true
    check result1.reusedExisting == false

    # Try to create same variant again
    let result2 = vm.createVariant(
      "firefox",
      "118.0",
      domains,
      compilerFlags
    )
    check result2.success == true
    check result2.reusedExisting == true

    # Fingerprints should match
    check result1.fingerprint.hash == result2.fingerprint.hash

  test "Query variant by path":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "nginx",
      "1.24.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Query by path
    let installPath = result.fingerprint.hash  # Simplified for test
    let query = vm.db.queryVariantByPath(result.fingerprint.hash)

    # Note: This will fail because we're not actually creating the path
    # In real usage, the path would be created during installation
    check query.found == false or query.record.packageName == "nginx"

  test "Variant reference tracking - add reference":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add reference from nginx to libssl variant
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")

    # Check reference was added
    let refs = vm.db.getVariantReferences(result.fingerprint.hash)
    check refs.len == 1
    check "nginx" in refs

  test "Variant reference tracking - multiple references":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add multiple references
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")
    vm.db.addVariantReference(result.fingerprint.hash, "curl")
    vm.db.addVariantReference(result.fingerprint.hash, "wget")

    # Check all references
    let refs = vm.db.getVariantReferences(result.fingerprint.hash)
    check refs.len == 3
    check "nginx" in refs
    check "curl" in refs
    check "wget" in refs

  test "Variant reference tracking - remove reference":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add and remove reference
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")
    check vm.db.getVariantReferences(result.fingerprint.hash).len == 1

    let removed = vm.db.removeVariantReference(result.fingerprint.hash, "nginx")
    check removed == true
    check vm.db.getVariantReferences(result.fingerprint.hash).len == 0

  test "Variant reference tracking - prevent deletion of referenced variant":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add reference
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")

    # Try to delete - should fail
    let canDelete = vm.db.canDeleteVariant(result.fingerprint.hash)
    check canDelete == false

    let deleteResult = vm.db.deleteVariantWithReferences(result.fingerprint.hash, force = false)
    check deleteResult.success == false
    check "referenced" in deleteResult.message.toLower()

  test "Variant reference tracking - allow deletion when no references":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # No references - should be deletable
    let canDelete = vm.db.canDeleteVariant(result.fingerprint.hash)
    check canDelete == true

    let deleteResult = vm.db.deleteVariantWithReferences(result.fingerprint.hash, force = false)
    check deleteResult.success == true

  test "Variant reference tracking - force delete referenced variant":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add reference
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")

    # Force delete
    let deleteResult = vm.db.deleteVariantWithReferences(result.fingerprint.hash, force = true)
    check deleteResult.success == true
    check "forced" in deleteResult.message.toLower()

  test "Variant reference info":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add references
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")
    vm.db.addVariantReference(result.fingerprint.hash, "curl")

    # Get reference info
    let refInfo = vm.db.getVariantReferenceInfo(result.fingerprint.hash)
    check refInfo.fingerprint == result.fingerprint.hash
    check refInfo.referencedBy.len == 2
    check refInfo.canDelete == false

  test "List variants referenced by package":
    let domains1 = {
      "init": @["dinit"]
    }.toTable

    let domains2 = {
      "init": @["systemd"]
    }.toTable

    # Create two variants
    let result1 = vm.createVariant("libssl", "3.0.0", domains1, compilerFlags)
    let result2 = vm.createVariant("zlib", "1.2.13", domains2, compilerFlags)

    check result1.success == true
    check result2.success == true

    # Add references from nginx to both
    vm.db.addVariantReference(result1.fingerprint.hash, "nginx")
    vm.db.addVariantReference(result2.fingerprint.hash, "nginx")

    # List variants referenced by nginx
    let nginxRefs = vm.db.listReferencedVariants("nginx")
    check nginxRefs.len == 2
    check result1.fingerprint.hash in nginxRefs
    check result2.fingerprint.hash in nginxRefs

  test "Reference persistence - save and load":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add references
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")
    vm.db.addVariantReference(result.fingerprint.hash, "curl")

    # Save
    vm.db.saveVariants()

    # Create new database instance and load
    let vm2 = newVariantManager(testDbPath)

    # Check references were loaded
    let refs = vm2.db.getVariantReferences(result.fingerprint.hash)
    check refs.len == 2
    check "nginx" in refs
    check "curl" in refs

  test "Duplicate reference prevention":
    let domains = {
      "init": @["dinit"]
    }.toTable

    let result = vm.createVariant(
      "libssl",
      "3.0.0",
      domains,
      compilerFlags
    )
    check result.success == true

    # Add same reference twice
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")
    vm.db.addVariantReference(result.fingerprint.hash, "nginx")

    # Should only have one reference
    let refs = vm.db.getVariantReferences(result.fingerprint.hash)
    check refs.len == 1
