## CAS Integration for Build Synthesis
##
## This module integrates the build synthesis system with the existing
## Content-Addressable Storage (CAS) system. It provides functions to:
## - Store synthesized builds in the CAS
## - Retrieve builds from the CAS
## - Track references to builds
## - Manage build artifacts and metadata

import std/[tables, strutils, times, options, os, algorithm]
import ../cas
import ../types
import ./build_synthesis
import ./variant_types

# Result type for error handling
type
  Result*[T, E] = object
    case isOk*: bool
    of true:
      value*: T
    of false:
      error*: E

template ok*[T](value: T): untyped =
  Result[T, string](isOk: true, value: value)

template err*[T](error: string): untyped =
  Result[T, string](isOk: false, error: error)

proc get*[T](res: Result[T, string]): T =
  if res.isOk:
    return res.value
  raise newException(ValueError, "Cannot get value from error result")

type
  # Reference tracking for builds
  BuildReference* = object
    buildHash*: string
    casHash*: Multihash
    packageName*: string
    packageVersion*: string
    timestamp*: times.Time
    refCount*: int

  # Build artifact metadata
  BuildArtifact* = object
    buildHash*: string
    casHash*: Multihash
    size*: int64
    compressed*: bool
    timestamp*: times.Time
    variantProfile*: VariantProfile

  # CAS integration manager
  CASIntegrationManager* = object
    casRoot*: string
    references*: Table[string, BuildReference]  # buildHash -> reference
    artifacts*: Table[string, BuildArtifact]    # buildHash -> artifact

# Constructor for CASIntegrationManager
proc newCASIntegrationManager*(casRoot: string): CASIntegrationManager =
  result.casRoot = casRoot
  result.references = initTable[string, BuildReference]()
  result.artifacts = initTable[string, BuildArtifact]()

# Store a synthesized build in the CAS
proc storeBuildInCAS*(
  manager: var CASIntegrationManager,
  buildResult: BuildSynthesisResult
): Result[Multihash, string] =
  ## Store a synthesized build in the CAS and track the reference
  ##
  ## This function:
  ## 1. Serializes the build configuration
  ## 2. Stores it in the CAS using BLAKE2b-512
  ## 3. Tracks the reference for garbage collection
  ## 4. Returns the CAS hash for retrieval
  
  try:
    # Serialize build configuration
    var serialized = ""
    serialized.add("package: " & buildResult.buildConfig.packageName & "\n")
    serialized.add("version: " & buildResult.buildConfig.packageVersion & "\n")
    serialized.add("variant: " & buildResult.buildConfig.variantProfile.toCanonical() & "\n")
    serialized.add("source_hash: " & buildResult.buildConfig.sourceHash & "\n")
    serialized.add("compiler: " & buildResult.buildConfig.compilerVersion & "\n")
    serialized.add("compiler_flags: " & buildResult.buildConfig.compilerFlags.sorted().join(",") & "\n")
    serialized.add("configure_flags: " & buildResult.buildConfig.configureFlags.sorted().join(",") & "\n")
    serialized.add("target_arch: " & buildResult.buildConfig.targetArchitecture & "\n")
    serialized.add("libc: " & buildResult.buildConfig.libc & "\n")
    serialized.add("allocator: " & buildResult.buildConfig.allocator & "\n")
    serialized.add("build_hash: " & buildResult.buildHash & "\n")
    
    # Store in CAS using existing system
    let casObject = storeObject(serialized, manager.casRoot, compress = true)
    
    # Track reference
    let reference = BuildReference(
      buildHash: buildResult.buildHash,
      casHash: casObject.hash,
      packageName: buildResult.buildConfig.packageName,
      packageVersion: buildResult.buildConfig.packageVersion,
      timestamp: getTime(),
      refCount: 1
    )
    
    manager.references[buildResult.buildHash] = reference
    
    # Track artifact
    let artifact = BuildArtifact(
      buildHash: buildResult.buildHash,
      casHash: casObject.hash,
      size: casObject.size,
      compressed: casObject.compressed,
      timestamp: casObject.timestamp,
      variantProfile: buildResult.buildConfig.variantProfile
    )
    
    manager.artifacts[buildResult.buildHash] = artifact
    
    return Result[Multihash, string](isOk: true, value: casObject.hash)
  
  except Exception as e:
    return Result[Multihash, string](isOk: false, error: "Failed to store build in CAS: " & e.msg)

# Retrieve a build from the CAS
proc retrieveBuildFromCAS*(
  manager: CASIntegrationManager,
  casHash: Multihash
): Result[BuildSynthesisResult, string] =
  ## Retrieve a synthesized build from the CAS
  ##
  ## This function:
  ## 1. Retrieves the build metadata from CAS
  ## 2. Verifies the hash matches
  ## 3. Reconstructs the build configuration
  ## 4. Returns the build result
  
  try:
    # Retrieve from CAS
    let data = retrieveObject(casHash, manager.casRoot)
    
    # Parse the serialized data
    var config = BuildConfig()
    var buildHash = ""
    var variantCanonical = ""
    var compilerFlagsStr = ""
    var configureFlagsStr = ""
    
    for line in data.split("\n"):
      if line.startsWith("package: "):
        config.packageName = line[9..^1]
      elif line.startsWith("version: "):
        config.packageVersion = line[9..^1]
      elif line.startsWith("variant: "):
        variantCanonical = line[9..^1]
      elif line.startsWith("source_hash: "):
        config.sourceHash = line[13..^1]
      elif line.startsWith("compiler: "):
        config.compilerVersion = line[10..^1]
      elif line.startsWith("compiler_flags: "):
        compilerFlagsStr = line[16..^1]
      elif line.startsWith("configure_flags: "):
        configureFlagsStr = line[17..^1]
      elif line.startsWith("target_arch: "):
        config.targetArchitecture = line[13..^1]
      elif line.startsWith("libc: "):
        config.libc = line[6..^1]
      elif line.startsWith("allocator: "):
        config.allocator = line[11..^1]
      elif line.startsWith("build_hash: "):
        buildHash = line[12..^1]
    
    # Reconstruct compiler and configure flags
    if compilerFlagsStr != "":
      config.compilerFlags = compilerFlagsStr.split(",")
    if configureFlagsStr != "":
      config.configureFlags = configureFlagsStr.split(",")
    
    # Reconstruct variant profile
    var reconstructedProfile = newVariantProfile()
    for domainPart in variantCanonical.split("|"):
      if domainPart.contains(":"):
        let parts = domainPart.split(":")
        let domainName = parts[0]
        let flagsStr = parts[1]
        for flag in flagsStr.split(","):
          if flag != "":
            reconstructedProfile.addFlag(domainName, flag)
    reconstructedProfile.calculateHash()
    config.variantProfile = reconstructedProfile
    
    # Verify hash matches
    let calculatedHash = calculateBuildHash(config)
    if calculatedHash != buildHash:
      return Result[BuildSynthesisResult, string](isOk: false, error: "Build hash mismatch: expected " & buildHash & ", got " & calculatedHash)
    
    return Result[BuildSynthesisResult, string](isOk: true, value: BuildSynthesisResult(
      buildHash: buildHash,
      casID: string(casHash),
      buildConfig: config,
      timestamp: getTime()
    ))
  
  except Exception as e:
    return Result[BuildSynthesisResult, string](isOk: false, error: "Failed to retrieve build from CAS: " & e.msg)

# Verify a build exists in the CAS
proc verifyBuildInCAS*(
  manager: CASIntegrationManager,
  buildHash: string
): bool =
  ## Verify that a build exists in the CAS
  
  if not manager.artifacts.hasKey(buildHash):
    return false
  
  let artifact = manager.artifacts[buildHash]
  
  try:
    # Try to retrieve the object
    discard retrieveObject(artifact.casHash, manager.casRoot)
    return true
  except:
    return false

# Increment reference count for a build
proc incrementReference*(
  manager: var CASIntegrationManager,
  buildHash: string
): Result[void, string] =
  ## Increment the reference count for a build
  
  if not manager.references.hasKey(buildHash):
    return Result[void, string](isOk: false, error: "Build not found: " & buildHash)
  
  manager.references[buildHash].refCount += 1
  return Result[void, string](isOk: true)

# Decrement reference count for a build
proc decrementReference*(
  manager: var CASIntegrationManager,
  buildHash: string
): Result[int, string] =
  ## Decrement the reference count for a build
  ## Returns the new reference count
  
  if not manager.references.hasKey(buildHash):
    return Result[int, string](isOk: false, error: "Build not found: " & buildHash)
  
  manager.references[buildHash].refCount -= 1
  return Result[int, string](isOk: true, value: manager.references[buildHash].refCount)

# Get reference count for a build
proc getReferenceCount*(
  manager: CASIntegrationManager,
  buildHash: string
): Option[int] =
  ## Get the reference count for a build
  
  if manager.references.hasKey(buildHash):
    return some(manager.references[buildHash].refCount)
  return none(int)

# List all tracked builds
proc listTrackedBuilds*(
  manager: CASIntegrationManager
): seq[BuildReference] =
  ## List all tracked builds
  
  result = @[]
  for buildHash, reference in manager.references:
    result.add(reference)

# Get artifact metadata for a build
proc getArtifactMetadata*(
  manager: CASIntegrationManager,
  buildHash: string
): Option[BuildArtifact] =
  ## Get artifact metadata for a build
  
  if manager.artifacts.hasKey(buildHash):
    return some(manager.artifacts[buildHash])
  return none(BuildArtifact)

# Calculate total size of tracked builds
proc getTotalTrackedSize*(
  manager: CASIntegrationManager
): int64 =
  ## Calculate total size of all tracked builds
  
  result = 0
  for buildHash, artifact in manager.artifacts:
    result += artifact.size

# String representation for display
proc `$`*(reference: BuildReference): string =
  ## Human-readable string representation
  
  result = "BuildReference(\n" &
    "  build_hash: " & reference.buildHash & "\n" &
    "  cas_hash: " & string(reference.casHash) & "\n" &
    "  package: " & reference.packageName & " " & reference.packageVersion & "\n" &
    "  ref_count: " & $reference.refCount & "\n" &
    ")"

proc `$`*(artifact: BuildArtifact): string =
  ## Human-readable string representation
  
  result = "BuildArtifact(\n" &
    "  build_hash: " & artifact.buildHash & "\n" &
    "  cas_hash: " & string(artifact.casHash) & "\n" &
    "  size: " & $artifact.size & " bytes\n" &
    "  compressed: " & $artifact.compressed & "\n" &
    ")"
