## Variant Hash Calculation
##
## This module implements deterministic hash calculation for variant profiles
## using xxh4-128 (or xxh3-128 until xxh4 is available).
##
## Philosophy:
## - Same variant profile ALWAYS produces same hash
## - Hash is deterministic across all platforms and runs
## - 128-bit output is collision-safe for any realistic number of variants
## - No cryptographic properties needed (no adversary in variant space)
##
## The hash enables:
## - Unique identification of build configurations
## - Content-addressable storage of builds
## - Reproducible build verification
## - Efficient deduplication

import std/[strutils, tables, sequtils, algorithm, sets]
import ./variant_types
# import ../xxhash  # For xxh3-128 (placeholder for xxh4) - imported via variant_types

proc calculateVariantHash*(profile: var VariantProfile): string =
  ## Calculate deterministic xxh4-128 hash of variant profile
  ## Uses lazy evaluation with caching from variant_types
  ##
  ## The hash is calculated from the canonical string representation,
  ## which is sorted alphabetically for determinism.
  ##
  ## Format: xxh4-<128-bit-hex> or xxh3-<128-bit-hex>
  ##
  ## Example:
  ##   Input: init:dinit|graphics:wayland,vulkan|optimization:lto
  ##   Output: xxh3-8f3c2d1e9a4b5c6d7e8f9a0b1c2d3e4f
  
  # Use lazy cached calculation from variant_types
  profile.calculateHash()
  result = profile.hash

proc updateHash*(profile: var VariantProfile) =
  ## Update the hash field of a variant profile
  ## Call this after modifying the profile
  ## Invalidates cache and recalculates
  
  profile.hash = ""  # Invalidate cache
  profile.calculateHash()

proc verifyHash*(profile: var VariantProfile): bool =
  ## Verify that the stored hash matches the calculated hash
  ## Returns true if hash is correct, false otherwise
  
  let storedHash = profile.hash
  profile.hash = ""  # Invalidate to force recalculation
  let calculatedHash = calculateVariantHash(profile)
  result = storedHash == calculatedHash

# Helper to create profile with hash
proc createVariantProfile*(domains: Table[string, VariantDomain]): VariantProfile =
  ## Create a variant profile with domains and calculate hash
  
  result.domains = domains
  result.updateHash()

proc inferDomain(flag: string): string =
  ## Infer domain from flag name (simple heuristic)
  ## This is a convenience for user-friendly syntax
  
  case flag:
  of "wayland", "x11", "vulkan", "opengl":
    "graphics"
  of "hardened", "selinux", "apparmor":
    "security"
  of "ipv6", "ipv4", "bluetooth", "wifi":
    "network"
  of "lto", "pgo", "native":
    "optimization"
  of "systemd", "dinit", "openrc", "runit":
    "init"
  else:
    "features"  # Default domain

# Parse variant string to profile
proc parseVariantString*(variantStr: string): VariantProfile =
  ## Parse variant string to profile
  ## Format: +flag1 +flag2 -flag3 domain:value
  ##
  ## Examples:
  ##   "+wayland +vulkan -X" → graphics:wayland,vulkan
  ##   "init:dinit" → init:dinit (exclusive)
  ##   "+hardened +ipv6" → security:hardened network:ipv6
  
  result = newVariantProfile()
  
  if variantStr.strip() == "":
    return result
  
  let parts = variantStr.split()
  
  for part in parts:
    if part.startsWith("+"):
      # Positive flag: +wayland → graphics:wayland
      let flag = part[1..^1]
      # Infer domain from flag name (simple heuristic)
      let domain = inferDomain(flag)
      result.addFlag(domain, flag)
    
    elif part.startsWith("-"):
      # Negative flag: -X → exclude X
      # For now, we don't store negative flags
      # They're used during resolution to filter
      discard
    
    elif ":" in part:
      # Explicit domain:value → init:dinit
      let colonPos = part.find(':')
      let domain = part[0..<colonPos]
      let value = part[colonPos+1..^1]
      
      # Check if domain should be exclusive
      let exclusivity = if domain in ["init", "libc", "allocator"]:
        Exclusive
      else:
        NonExclusive
      
      if not result.hasDomain(domain):
        result.addDomain(newVariantDomain(domain, exclusivity))
      
      result.addFlag(domain, value)
  
  result.updateHash()

# Format variant profile for display
proc formatVariant*(profile: VariantProfile): string =
  ## Format variant profile for human-readable display
  ## Example: +wayland +vulkan init:dinit optimization:lto
  
  var parts: seq[string] = @[]
  
  for domainName in toSeq(profile.domains.keys).sorted():
    let domain = profile.domains[domainName]
    
    if domain.exclusivity == Exclusive:
      # Exclusive: show as domain:value
      for flag in toSeq(domain.flags).sorted():
        parts.add(domainName & ":" & flag)
    else:
      # Non-exclusive: show as +flag
      for flag in toSeq(domain.flags).sorted():
        parts.add("+" & flag)
  
  result = parts.join(" ")
