## Integration Tests for CAS Integration
##
## Tests for the CAS integration module which bridges build synthesis
## with the existing Content-Addressable Storage system.

import std/[unittest, os, tempfiles, times, tables, options]
import ../src/nip/resolver/build_synthesis
import ../src/nip/resolver/variant_types
import ../src/nip/resolver/cas_integration

# Helper to count table entries
proc tableLen[K, V](t: Table[K, V]): int =
  result = 0
  for _ in t.keys:
    result += 1

suite "CAS Integration Tests":
  
  test "Create CAS integration manager":
    ## Test creating a CAS integration manager
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      let manager = newCASIntegrationManager(casRoot)
      
      check manager.casRoot == casRoot
      check tableLen(manager.references) == 0
      check tableLen(manager.artifacts) == 0
    
    finally:
      removeDir(casRoot)

  test "Store build in CAS":
    ## Test storing a synthesized build in the CAS
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create variant profile
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.calculateHash()
      
      # Synthesize build
      let buildResult = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123"
      )
      
      # Store in CAS
      let storeResult = manager.storeBuildInCAS(buildResult)
      
      check storeResult.isOk
      check tableLen(manager.references) == 1
      check tableLen(manager.artifacts) == 1
    
    finally:
      removeDir(casRoot)

  test "Retrieve build from CAS":
    ## Test retrieving a stored build from the CAS
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store build
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.calculateHash()
      
      let buildResult = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123"
      )
      
      let storeResult = manager.storeBuildInCAS(buildResult)
      check storeResult.isOk
      
      let casHash = storeResult.get()
      
      # Retrieve from CAS
      let retrieveResult = manager.retrieveBuildFromCAS(casHash)
      
      check retrieveResult.isOk
      let retrieved = retrieveResult.get()
      check retrieved.buildHash == buildResult.buildHash
      check retrieved.buildConfig.packageName == "nginx"
      check retrieved.buildConfig.packageVersion == "1.24.0"
    
    finally:
      removeDir(casRoot)

  test "Verify build in CAS":
    ## Test verifying that a build exists in the CAS
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store build
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.calculateHash()
      
      let buildResult = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123"
      )
      
      let storeResult = manager.storeBuildInCAS(buildResult)
      check storeResult.isOk
      
      # Verify build exists
      check manager.verifyBuildInCAS(buildResult.buildHash) == true
      
      # Verify non-existent build
      check manager.verifyBuildInCAS("xxh3-nonexistent") == false
    
    finally:
      removeDir(casRoot)

  test "Reference counting":
    ## Test reference counting for builds
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store build
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.calculateHash()
      
      let buildResult = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123"
      )
      
      let storeResult = manager.storeBuildInCAS(buildResult)
      check storeResult.isOk
      
      # Check initial reference count
      let initialCount = manager.getReferenceCount(buildResult.buildHash)
      var hasInitial = false
      if initialCount.isSome:
        hasInitial = true
      check hasInitial
      check initialCount.get() == 1
      
      # Increment reference
      let incResult = manager.incrementReference(buildResult.buildHash)
      check incResult.isOk
      
      let newCount = manager.getReferenceCount(buildResult.buildHash)
      var hasNew = false
      if newCount.isSome:
        hasNew = true
      check hasNew
      check newCount.get() == 2
      
      # Decrement reference
      let decResult = manager.decrementReference(buildResult.buildHash)
      check decResult.isOk
      check decResult.get() == 1
    
    finally:
      removeDir(casRoot)

  test "List tracked builds":
    ## Test listing all tracked builds
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store multiple builds
      for i in 0..<3:
        var profile = newVariantProfile()
        profile.addFlag("optimization", "lto")
        profile.calculateHash()
        
        let buildResult = synthesizeBuild(
          packageName = "package" & $i,
          packageVersion = "1.0.0",
          variantProfile = profile,
          sourceHash = "blake3-" & $i
        )
        
        discard manager.storeBuildInCAS(buildResult)
      
      # List tracked builds
      let tracked = manager.listTrackedBuilds()
      check tracked.len() == 3
    
    finally:
      removeDir(casRoot)

  test "Get artifact metadata":
    ## Test retrieving artifact metadata
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store build
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.calculateHash()
      
      let buildResult = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123"
      )
      
      let storeResult = manager.storeBuildInCAS(buildResult)
      check storeResult.isOk
      
      # Get artifact metadata
      let metadata = manager.getArtifactMetadata(buildResult.buildHash)
      var hasMetadata = false
      if metadata.isSome:
        hasMetadata = true
      check hasMetadata
      
      let artifact = metadata.get()
      check artifact.buildHash == buildResult.buildHash
      check artifact.compressed == true
      check artifact.size > 0
    
    finally:
      removeDir(casRoot)

  test "Calculate total tracked size":
    ## Test calculating total size of tracked builds
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create and store multiple builds
      for i in 0..<3:
        var profile = newVariantProfile()
        profile.addFlag("optimization", "lto")
        profile.calculateHash()
        
        let buildResult = synthesizeBuild(
          packageName = "package" & $i,
          packageVersion = "1.0.0",
          variantProfile = profile,
          sourceHash = "blake3-" & $i
        )
        
        discard manager.storeBuildInCAS(buildResult)
      
      # Calculate total size
      let totalSize = manager.getTotalTrackedSize()
      check totalSize > 0
    
    finally:
      removeDir(casRoot)

  test "Multiple builds with same package":
    ## Test storing multiple builds of the same package with different variants
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create two builds with different variants
      var profile1 = newVariantProfile()
      profile1.addFlag("optimization", "lto")
      profile1.calculateHash()
      
      var profile2 = newVariantProfile()
      profile2.addFlag("optimization", "o3")
      profile2.calculateHash()
      
      let build1 = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile1,
        sourceHash = "blake3-abc123"
      )
      
      let build2 = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile2,
        sourceHash = "blake3-abc123"
      )
      
      # Store both builds
      let store1 = manager.storeBuildInCAS(build1)
      let store2 = manager.storeBuildInCAS(build2)
      
      check store1.isOk
      check store2.isOk
      
      # Verify both are tracked
      check tableLen(manager.references) == 2
      check tableLen(manager.artifacts) == 2
      
      # Verify they have different hashes
      check build1.buildHash != build2.buildHash
    
    finally:
      removeDir(casRoot)

  test "Round-trip: store and retrieve":
    ## Test complete round-trip: store build, retrieve it, verify it matches
    
    let casRoot = getTempDir() / "test_cas_" & $getTime().toUnix()
    createDir(casRoot)
    
    try:
      var manager = newCASIntegrationManager(casRoot)
      
      # Create build with specific configuration
      var profile = newVariantProfile()
      profile.addFlag("optimization", "lto")
      profile.addFlag("security", "hardened")
      profile.calculateHash()
      
      let original = synthesizeBuild(
        packageName = "nginx",
        packageVersion = "1.24.0",
        variantProfile = profile,
        sourceHash = "blake3-abc123",
        compilerVersion = "gcc-13.2.0",
        compilerFlags = @["-O2", "-march=native"],
        configureFlags = @["--with-ssl", "--with-http2"],
        targetArchitecture = "x86_64",
        libc = "musl",
        allocator = "jemalloc"
      )
      
      # Store in CAS
      let storeResult = manager.storeBuildInCAS(original)
      check storeResult.isOk
      
      let casHash = storeResult.get()
      
      # Retrieve from CAS
      let retrieveResult = manager.retrieveBuildFromCAS(casHash)
      check retrieveResult.isOk
      
      let retrieved = retrieveResult.get()
      
      # Verify all fields match
      check retrieved.buildHash == original.buildHash
      check retrieved.buildConfig.packageName == original.buildConfig.packageName
      check retrieved.buildConfig.packageVersion == original.buildConfig.packageVersion
      check retrieved.buildConfig.sourceHash == original.buildConfig.sourceHash
      check retrieved.buildConfig.compilerVersion == original.buildConfig.compilerVersion
      check retrieved.buildConfig.targetArchitecture == original.buildConfig.targetArchitecture
      check retrieved.buildConfig.libc == original.buildConfig.libc
      check retrieved.buildConfig.allocator == original.buildConfig.allocator
    
    finally:
      removeDir(casRoot)
