## End-to-End Integration Tests
##
## This test suite validates the complete dependency resolution workflow
## from CLI input to final resolution result, testing all components
## working together.

import unittest
import tables
import ../src/nip/resolver/orchestrator
import ../src/nip/resolver/types
import ../src/nip/cas/storage

suite "End-to-End Resolution Workflow":
  test "Complete resolution workflow":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-1")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    # Create variant demand
    let demand = VariantDemand(
      useFlags: @["ssl", "http2"],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @["-O2", "-march=native"]
    )
    
    # Resolve dependencies
    let result = orch.resolve("nginx", ">=1.24.0", demand)
    
    # Verify success
    check result.isOk
    
    let resolution = result.get
    check resolution.resolutionTime >= 0.0
    check resolution.cacheHit == false  # First resolution
    
    # Verify metrics updated
    let metrics = orch.getMetrics()
    check metrics.totalResolutions == 1
    check metrics.successfulResolutions == 1
    check metrics.cacheMisses == 1
  
  test "Cache hit on repeated resolution":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-2")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # First resolution (cache miss)
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    check result1.get.cacheHit == false
    
    # Second resolution (cache hit)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    check result2.get.cacheHit == true
    
    # Verify cache speedup
    check result2.get.resolutionTime < result1.get.resolutionTime
    
    # Verify metrics
    let metrics = orch.getMetrics()
    check metrics.totalResolutions == 2
    check metrics.cacheHits == 1
    check metrics.cacheMisses == 1
  
  test "Different variants produce different resolutions":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-3")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    # Resolve with first variant
    let demand1 = VariantDemand(
      useFlags: @["ssl"],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    let result1 = orch.resolve("nginx", "*", demand1)
    check result1.isOk
    
    # Resolve with second variant
    let demand2 = VariantDemand(
      useFlags: @["ssl", "http2"],  # Different USE flags
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    let result2 = orch.resolve("nginx", "*", demand2)
    check result2.isOk
    
    # Both should be cache misses (different variants)
    check result1.get.cacheHit == false
    check result2.get.cacheHit == false
    
    # Verify metrics
    let metrics = orch.getMetrics()
    check metrics.cacheMisses == 2
    check metrics.cacheHits == 0

suite "Cache Invalidation Workflow":
  test "Repository update invalidates cache":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-4")
    let repos1: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos1, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # First resolution
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    check result1.get.cacheHit == false
    
    # Second resolution (cache hit)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    check result2.get.cacheHit == true
    
    # Update repositories (simulates metadata change)
    let repos2 = @[
      Repository(
        name: "main",
        packages: @[]
      )
    ]
    orch.updateRepositories(repos2)
    
    # Third resolution (cache miss due to invalidation)
    let result3 = orch.resolve("test-pkg", "*", demand)
    check result3.isOk
    check result3.get.cacheHit == false
    
    # Verify metrics
    let metrics = orch.getMetrics()
    check metrics.totalResolutions == 3
    check metrics.cacheHits == 1
    check metrics.cacheMisses == 2

suite "Configuration Management Workflow":
  test "Disable cache affects behavior":
    # Setup with cache enabled
    let cas = newCASStorage("/tmp/test-e2e-5")
    let repos: seq[Repository] = @[]
    var config = defaultConfig()
    config.enableCache = true
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # First resolution (cache miss)
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    check result1.get.cacheHit == false
    
    # Second resolution (cache hit)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    check result2.get.cacheHit == true
    
    # Disable cache
    config.enableCache = false
    orch.updateConfig(config)
    
    # Third resolution (cache disabled, always miss)
    let result3 = orch.resolve("test-pkg", "*", demand)
    check result3.isOk
    check result3.get.cacheHit == false
  
  test "Clear cache resets state":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-6")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # Populate cache
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    
    # Verify cache hit
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.get.cacheHit == true
    
    # Clear cache
    orch.clearCache()
    
    # Verify cache miss
    let result3 = orch.resolve("test-pkg", "*", demand)
    check result3.get.cacheHit == false

suite "Metrics Tracking Workflow":
  test "Metrics track complete workflow":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-7")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # Initial metrics
    var metrics = orch.getMetrics()
    check metrics.totalResolutions == 0
    check metrics.cacheHits == 0
    check metrics.cacheMisses == 0
    
    # First resolution
    discard orch.resolve("pkg1", "*", demand)
    metrics = orch.getMetrics()
    check metrics.totalResolutions == 1
    check metrics.cacheMisses == 1
    
    # Second resolution (same package, cache hit)
    discard orch.resolve("pkg1", "*", demand)
    metrics = orch.getMetrics()
    check metrics.totalResolutions == 2
    check metrics.cacheHits == 1
    
    # Third resolution (different package, cache miss)
    discard orch.resolve("pkg2", "*", demand)
    metrics = orch.getMetrics()
    check metrics.totalResolutions == 3
    check metrics.cacheMisses == 2
    
    # Verify final state
    check metrics.successfulResolutions == 3
    check metrics.failedResolutions == 0
  
  test "Reset metrics clears counters":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-8")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # Do some resolutions
    for i in 0..<5:
      discard orch.resolve(fmt"pkg-{i}", "*", demand)
    
    # Verify metrics
    var metrics = orch.getMetrics()
    check metrics.totalResolutions == 5
    
    # Reset metrics
    orch.resetMetrics()
    
    # Verify reset
    metrics = orch.getMetrics()
    check metrics.totalResolutions == 0
    check metrics.cacheHits == 0
    check metrics.cacheMisses == 0

suite "Performance Characteristics":
  test "Cache provides speedup":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-9")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # First resolution (cold cache)
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    let coldTime = result1.get.resolutionTime
    
    # Second resolution (warm cache)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    let warmTime = result2.get.resolutionTime
    
    # Verify speedup
    check warmTime < coldTime
    
    let speedup = coldTime / warmTime
    echo fmt"  Cache speedup: {speedup:.2f}x"
    echo fmt"  Cold: {coldTime * 1000:.2f}ms, Warm: {warmTime * 1000:.2f}ms"
  
  test "Multiple resolutions maintain performance":
    # Setup
    let cas = newCASStorage("/tmp/test-e2e-10")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @[],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # Resolve multiple packages
    var totalTime = 0.0
    for i in 0..<10:
      let result = orch.resolve(fmt"pkg-{i}", "*", demand)
      check result.isOk
      totalTime += result.get.resolutionTime
    
    let avgTime = totalTime / 10.0
    echo fmt"  Average resolution time: {avgTime * 1000:.2f}ms"
    
    # Verify reasonable performance
    check avgTime < 1.0  # Should be < 1 second per resolution

suite "Integration with Components":
  test "Orchestrator integrates all components":
    # This test verifies that the orchestrator properly coordinates
    # all resolver components in the correct order
    
    let cas = newCASStorage("/tmp/test-e2e-11")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand = VariantDemand(
      useFlags: @["ssl", "http2"],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @["-O2"]
    )
    
    # Resolve
    let result = orch.resolve("nginx", ">=1.24.0", demand)
    
    # Verify all components were invoked
    check result.isOk
    
    # Verify resolution result structure
    let resolution = result.get
    check resolution.graph.rootPackage.name == "nginx"
    check resolution.installOrder.len >= 0
    check resolution.resolutionTime >= 0.0
    
    # Verify cache was used
    let cacheMetrics = orch.getCacheMetrics()
    check cacheMetrics.l1Capacity == 100
