## Unit Tests for Source Selection Strategy
##
## Tests for the source selection logic that chooses the best
## source adapter based on strategy (PreferBinary, PreferSource, Balanced).

import std/[unittest, options, strutils]
import ../src/nip/resolver/source_adapter
import ../src/nip/resolver/frozen_adapter
import ../src/nip/resolver/flexible_adapter
import ../src/nip/resolver/variant_types

suite "Source Selection Strategy Tests":
  
  test "PreferBinary: Chooses frozen when available":
    ## Test that PreferBinary strategy prefers frozen sources
    
    # Create frozen adapter with package
    let frozenAdapter = newFrozenAdapter("nix", priority = 50)
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let frozenMetadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "nix-store-hash",
      buildTime: 0
    )
    frozenAdapter.addPackage(frozenMetadata)
    
    # Create flexible adapter with same package
    let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30)
    let flexibleMetadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz",
      buildTime: 300
    )
    flexibleAdapter.addPackage(flexibleMetadata)
    
    # Create demand
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    # Select source with PreferBinary strategy
    let adapters = @[SourceAdapter(frozenAdapter), SourceAdapter(flexibleAdapter)]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    check selection.isSome
    check selection.get.adapter.name == "nix"
    check selection.get.adapter.class == Frozen
    check selection.get.estimatedTime == 0
    check "binary" in selection.get.reason.toLowerAscii()
  
  test "PreferBinary: Falls back to flexible when no frozen available":
    ## Test that PreferBinary falls back to flexible sources
    
    # Create only flexible adapter
    let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30)
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz",
      buildTime: 300
    )
    flexibleAdapter.addPackage(metadata)
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    let adapters = @[SourceAdapter(flexibleAdapter)]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    check selection.isSome
    check selection.get.adapter.name == "gentoo"
    check selection.get.adapter.class == Flexible
    check selection.get.estimatedTime == 300
    check "source" in selection.get.reason.toLowerAscii()
  
  test "PreferSource: Always chooses flexible when available":
    ## Test that PreferSource strategy prefers flexible sources
    
    # Create both frozen and flexible adapters
    let frozenAdapter = newFrozenAdapter("nix", priority = 50)
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let frozenMetadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "nix-store-hash",
      buildTime: 0
    )
    frozenAdapter.addPackage(frozenMetadata)
    
    let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30)
    let flexibleMetadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz",
      buildTime: 300
    )
    flexibleAdapter.addPackage(flexibleMetadata)
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    # Select with PreferSource strategy
    let adapters = @[SourceAdapter(frozenAdapter), SourceAdapter(flexibleAdapter)]
    let selection = selectSource(adapters, demand, PreferSource)
    
    check selection.isSome
    check selection.get.adapter.name == "gentoo"
    check selection.get.adapter.class == Flexible
    check selection.get.estimatedTime == 300
    check "source" in selection.get.reason.toLowerAscii()
  
  test "PreferSource: Falls back to frozen when no flexible available":
    ## Test that PreferSource falls back to frozen sources
    
    # Create only frozen adapter
    let frozenAdapter = newFrozenAdapter("nix", priority = 50)
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "nix-store-hash",
      buildTime: 0
    )
    frozenAdapter.addPackage(metadata)
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    let adapters = @[SourceAdapter(frozenAdapter)]
    let selection = selectSource(adapters, demand, PreferSource)
    
    check selection.isSome
    check selection.get.adapter.name == "nix"
    check selection.get.adapter.class == Frozen
    check selection.get.estimatedTime == 0
  
  test "Balanced: Considers priority":
    ## Test that Balanced strategy respects adapter priority
    
    # Create two frozen adapters with different priorities
    let highPriorityAdapter = newFrozenAdapter("nix", priority = 100)
    let lowPriorityAdapter = newFrozenAdapter("arch", priority = 50)
    
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "hash",
      buildTime: 0
    )
    
    highPriorityAdapter.addPackage(metadata)
    lowPriorityAdapter.addPackage(metadata)
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    # Select with Balanced strategy
    let adapters = @[SourceAdapter(lowPriorityAdapter), SourceAdapter(highPriorityAdapter)]
    let selection = selectSource(adapters, demand, Balanced)
    
    check selection.isSome
    check selection.get.adapter.name == "nix"
    check selection.get.adapter.priority == 100
  
  test "No source available returns None":
    ## Test that selectSource returns None when no source can satisfy
    
    let frozenAdapter = newFrozenAdapter("nix", priority = 50)
    
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    # Don't add any packages to adapter
    
    let demand = VariantDemand(
      packageName: "nonexistent",
      variantProfile: profile,
      optional: false
    )
    
    let adapters = @[SourceAdapter(frozenAdapter)]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    check selection.isNone
  
  test "Empty adapter list returns None":
    ## Test that selectSource handles empty adapter list
    
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    let adapters: seq[SourceAdapter] = @[]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    check selection.isNone
  
  test "Multiple adapters with same priority":
    ## Test behavior when multiple adapters have same priority
    
    let adapter1 = newFrozenAdapter("nix", priority = 50)
    let adapter2 = newFrozenAdapter("arch", priority = 50)
    
    var profile = newVariantProfile()
    profile.addFlag("optimization", "lto")
    profile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[profile],
      sourceHash: "hash",
      buildTime: 0
    )
    
    adapter1.addPackage(metadata)
    adapter2.addPackage(metadata)
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: profile,
      optional: false
    )
    
    # With Balanced strategy, should pick one deterministically
    let adapters = @[SourceAdapter(adapter1), SourceAdapter(adapter2)]
    let selection = selectSource(adapters, demand, Balanced)
    
    check selection.isSome
    # Should pick one of them (deterministic based on order)
    check selection.get.adapter.name in ["nix", "arch"]
  
  test "Frozen adapter with wrong variant is skipped":
    ## Test that frozen adapters with wrong variants are not selected
    
    let frozenAdapter = newFrozenAdapter("nix", priority = 50)
    
    var availableProfile = newVariantProfile()
    availableProfile.addFlag("optimization", "lto")
    availableProfile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[availableProfile],
      sourceHash: "hash",
      buildTime: 0
    )
    frozenAdapter.addPackage(metadata)
    
    # Request different variant
    var demandProfile = newVariantProfile()
    demandProfile.addFlag("optimization", "pgo")
    demandProfile.calculateHash()
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: demandProfile,
      optional: false
    )
    
    let adapters = @[SourceAdapter(frozenAdapter)]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    # Frozen adapter can't satisfy wrong variant
    check selection.isNone
  
  test "Flexible adapter accepts any variant":
    ## Test that flexible adapters can satisfy any variant
    
    let flexibleAdapter = newFlexibleAdapter("gentoo", priority = 30)
    
    var availableProfile = newVariantProfile()
    availableProfile.addFlag("optimization", "lto")
    availableProfile.calculateHash()
    
    let metadata = PackageMetadata(
      name: "nginx",
      version: "1.24.0",
      availableVariants: @[availableProfile],
      sourceHash: "https://nginx.org/nginx-1.24.0.tar.gz",
      buildTime: 300
    )
    flexibleAdapter.addPackage(metadata)
    
    # Request different variant
    var demandProfile = newVariantProfile()
    demandProfile.addFlag("optimization", "pgo")
    demandProfile.addFlag("graphics", "wayland")
    demandProfile.calculateHash()
    
    let demand = VariantDemand(
      packageName: "nginx",
      variantProfile: demandProfile,
      optional: false
    )
    
    let adapters = @[SourceAdapter(flexibleAdapter)]
    let selection = selectSource(adapters, demand, PreferBinary)
    
    # Flexible adapter can satisfy any variant
    check selection.isSome
    check selection.get.adapter.name == "gentoo"
    check selection.get.estimatedTime == 300

