## Tests for Resolution Orchestrator
##
## This test suite verifies the orchestrator's ability to coordinate
## all resolver components and provide a unified resolution interface.

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

suite "Orchestrator Construction":
  test "Create with default config":
    let cas = newCASStorage("/tmp/test-orch-1")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    check orch.getConfig().enableCache == true
    check orch.getConfig().enableParallel == false
    check orch.getConfig().maxRetries == 3
    check orch.getConfig().l1CacheCapacity == 100
  
  test "Create with custom config":
    let cas = newCASStorage("/tmp/test-orch-2")
    let repos: seq[Repository] = @[]
    let config = ResolverConfig(
      enableCache: false,
      enableParallel: true,
      maxRetries: 5,
      timeout: initDuration(seconds = 600),
      l1CacheCapacity: 200
    )
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    check orch.getConfig().enableCache == false
    check orch.getConfig().enableParallel == true
    check orch.getConfig().maxRetries == 5
    check orch.getConfig().l1CacheCapacity == 200

suite "Basic Resolution":
  test "Resolve simple package":
    let cas = newCASStorage("/tmp/test-orch-3")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let result = orch.resolve(
      "test-pkg",
      "*",
      VariantDemand(
        useFlags: @[],
        libc: "musl",
        allocator: "jemalloc",
        targetArch: "x86_64",
        buildFlags: @[]
      )
    )
    
    check result.isOk
    check result.get.cacheHit == false
    check result.get.resolutionTime >= 0.0
  
  test "Resolve with version constraint":
    let cas = newCASStorage("/tmp/test-orch-4")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let result = orch.resolve(
      "nginx",
      ">=1.24.0",
      VariantDemand(
        useFlags: @["ssl", "http2"],
        libc: "musl",
        allocator: "jemalloc",
        targetArch: "x86_64",
        buildFlags: @[]
      )
    )
    
    check result.isOk
  
  test "Resolve with USE flags":
    let cas = newCASStorage("/tmp/test-orch-5")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let result = orch.resolve(
      "firefox",
      "*",
      VariantDemand(
        useFlags: @["wayland", "pulseaudio", "alsa"],
        libc: "glibc",
        allocator: "default",
        targetArch: "x86_64",
        buildFlags: @["-O3", "-march=native"]
      )
    )
    
    check result.isOk

suite "Caching Behavior":
  test "Cache miss on first resolution":
    let cas = newCASStorage("/tmp/test-orch-6")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let result = orch.resolve(
      "test-pkg",
      "*",
      VariantDemand(
        useFlags: @[],
        libc: "musl",
        allocator: "jemalloc",
        targetArch: "x86_64",
        buildFlags: @[]
      )
    )
    
    check result.isOk
    check result.get.cacheHit == false
    
    let metrics = orch.getMetrics()
    check metrics.cacheMisses == 1
    check metrics.cacheHits == 0
  
  test "Cache hit on second resolution":
    let cas = newCASStorage("/tmp/test-orch-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: @[]
    )
    
    # First resolution
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    check result1.get.cacheHit == false
    
    # Second resolution (should hit cache)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    check result2.get.cacheHit == true
    
    let metrics = orch.getMetrics()
    check metrics.cacheHits == 1
    check metrics.cacheMisses == 1
  
  test "Different variants produce different cache keys":
    let cas = newCASStorage("/tmp/test-orch-8")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let demand1 = VariantDemand(
      useFlags: @["ssl"],
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    let demand2 = VariantDemand(
      useFlags: @["ssl", "http2"],  # Different USE flags
      libc: "musl",
      allocator: "jemalloc",
      targetArch: "x86_64",
      buildFlags: @[]
    )
    
    # Resolve with first variant
    let result1 = orch.resolve("nginx", "*", demand1)
    check result1.isOk
    check result1.get.cacheHit == false
    
    # Resolve with second variant (different cache key)
    let result2 = orch.resolve("nginx", "*", demand2)
    check result2.isOk
    check result2.get.cacheHit == false
    
    let metrics = orch.getMetrics()
    check metrics.cacheMisses == 2
    check metrics.cacheHits == 0
  
  test "Disabled cache always misses":
    let cas = newCASStorage("/tmp/test-orch-9")
    let repos: seq[Repository] = @[]
    let config = ResolverConfig(
      enableCache: false,
      enableParallel: false,
      maxRetries: 3,
      timeout: initDuration(seconds = 300),
      l1CacheCapacity: 100
    )
    
    let orch = newResolutionOrchestrator(cas, repos, 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 disabled, should still miss)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.isOk
    check result2.get.cacheHit == false

suite "Metrics Tracking":
  test "Track total resolutions":
    let cas = newCASStorage("/tmp/test-orch-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: @[]
    )
    
    for i in 0..<5:
      discard orch.resolve(fmt"pkg-{i}", "*", demand)
    
    let metrics = orch.getMetrics()
    check metrics.totalResolutions == 5
    check metrics.successfulResolutions == 5
  
  test "Track cache hit rate":
    let cas = newCASStorage("/tmp/test-orch-11")
    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 (miss)
    discard orch.resolve("test-pkg", "*", demand)
    
    # Three more resolutions (hits)
    for i in 0..<3:
      discard orch.resolve("test-pkg", "*", demand)
    
    let metrics = orch.getMetrics()
    check metrics.totalResolutions == 4
    check metrics.cacheHits == 3
    check metrics.cacheMisses == 1
  
  test "Reset metrics":
    let cas = newCASStorage("/tmp/test-orch-12")
    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..<3:
      discard orch.resolve(fmt"pkg-{i}", "*", demand)
    
    check orch.getMetrics().totalResolutions == 3
    
    # Reset metrics
    orch.resetMetrics()
    
    check orch.getMetrics().totalResolutions == 0
    check orch.getMetrics().cacheHits == 0
    check orch.getMetrics().cacheMisses == 0

suite "Configuration Management":
  test "Update configuration":
    let cas = newCASStorage("/tmp/test-orch-13")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    check orch.getConfig().enableCache == true
    
    var newConfig = config
    newConfig.enableCache = false
    
    orch.updateConfig(newConfig)
    
    check orch.getConfig().enableCache == false
  
  test "Configuration affects behavior":
    let cas = newCASStorage("/tmp/test-orch-14")
    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 with cache enabled
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.isOk
    
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.get.cacheHit == true
    
    # Disable cache
    var newConfig = orch.getConfig()
    newConfig.enableCache = false
    orch.updateConfig(newConfig)
    
    # Resolve with cache disabled
    let result3 = orch.resolve("test-pkg", "*", demand)
    check result3.get.cacheHit == false

suite "Cache Management":
  test "Clear cache":
    let cas = newCASStorage("/tmp/test-orch-15")
    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
    discard orch.resolve("test-pkg", "*", demand)
    
    # Verify cache hit
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.get.cacheHit == true
    
    # Clear cache
    orch.clearCache()
    
    # Verify cache miss
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.get.cacheHit == false
  
  test "Get cache metrics":
    let cas = newCASStorage("/tmp/test-orch-16")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    let cacheMetrics = orch.getCacheMetrics()
    check cacheMetrics.l1Size == 0
    check cacheMetrics.l1Capacity == 100

suite "Repository Management":
  test "Update repositories":
    let cas = newCASStorage("/tmp/test-orch-17")
    let repos: seq[Repository] = @[]
    let config = defaultConfig()
    
    let orch = newResolutionOrchestrator(cas, repos, config)
    
    check orch.getRepositories().len == 0
    
    let newRepos = @[
      Repository(
        name: "main",
        packages: @[]
      )
    ]
    
    orch.updateRepositories(newRepos)
    
    check orch.getRepositories().len == 1
    check orch.getRepositories()[0].name == "main"
  
  test "Repository update invalidates cache":
    let cas = newCASStorage("/tmp/test-orch-18")
    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
    discard orch.resolve("test-pkg", "*", demand)
    
    # Verify cache hit
    let result1 = orch.resolve("test-pkg", "*", demand)
    check result1.get.cacheHit == true
    
    # Update repositories (should invalidate cache)
    let newRepos = @[
      Repository(
        name: "main",
        packages: @[]
      )
    ]
    orch.updateRepositories(newRepos)
    
    # Verify cache miss (invalidated)
    let result2 = orch.resolve("test-pkg", "*", demand)
    check result2.get.cacheHit == false
