// JIT Compilation Strategy
//
// This module provides:
// 1. Compilation mode selection (interpret, baseline, optimized)
// 2. Lazy compilation (compile on demand)
// 3. Tiered compilation strategy

// ============ Compilation Mode ============

///|
/// The compilation mode for a function
pub(all) enum CompilationMode {
  // Always interpret (never JIT compile)
  Interpret
  // Baseline JIT (fast compilation, less optimization)
  Baseline
  // Optimized JIT (slow compilation, more optimization)
  Optimized
}

///|
fn CompilationMode::to_string(self : CompilationMode) -> String {
  match self {
    Interpret => "interpret"
    Baseline => "baseline"
    Optimized => "optimized"
  }
}

///|
pub impl Show for CompilationMode with fn output(self, logger) {
  logger.write_string(self.to_string())
}

// ============ Compilation Request ============

///|
/// A request to compile a function
pub(all) struct CompilationRequest {
  // Function index in the module
  func_idx : Int
  // Requested compilation mode
  mode : CompilationMode
  // Priority (higher = compile sooner)
  priority : Int
}

///|
pub fn CompilationRequest::CompilationRequest(
  func_idx : Int,
  mode : CompilationMode,
) -> CompilationRequest {
  { func_idx, mode, priority: 0 }
}

///|
pub fn CompilationRequest::with_priority(
  func_idx : Int,
  mode : CompilationMode,
  priority : Int,
) -> CompilationRequest {
  { func_idx, mode, priority }
}

// ============ Compilation Queue ============

///|
/// Queue of pending compilation requests
pub(all) struct CompilationQueue {
  // Pending requests
  requests : Array[CompilationRequest]
  // Maximum queue size (0 = unlimited)
  max_size : Int
}

///|
pub fn CompilationQueue::CompilationQueue(max_size : Int) -> CompilationQueue {
  { requests: [], max_size }
}

///|
/// Add a compilation request to the queue
pub fn CompilationQueue::enqueue(
  self : CompilationQueue,
  request : CompilationRequest,
) -> Bool {
  // Check for duplicates
  for req in self.requests {
    if req.func_idx == request.func_idx {
      return false
    }
  }
  // Check size limit
  if self.max_size > 0 && self.requests.length() >= self.max_size {
    return false
  }
  // Insert sorted by priority (highest first)
  let mut inserted = false
  for i in 0.. self.requests[i].priority {
      // Insert at position i
      let old = self.requests
      let new_arr : Array[CompilationRequest] = []
      for j in 0.. CompilationRequest? {
  if self.requests.length() == 0 {
    return None
  }
  // Pop first (highest priority)
  let first = self.requests[0]
  let new_arr : Array[CompilationRequest] = []
  for i in 1.. Bool {
  for req in self.requests {
    if req.func_idx == func_idx {
      return true
    }
  }
  false
}

///|
/// Get queue length
pub fn CompilationQueue::length(self : CompilationQueue) -> Int {
  self.requests.length()
}

///|
/// Check if queue is empty
pub fn CompilationQueue::is_empty(self : CompilationQueue) -> Bool {
  self.requests.length() == 0
}

///|
/// Clear the queue
pub fn CompilationQueue::clear(self : CompilationQueue) -> Unit {
  self.requests.clear()
}

// ============ Tiered Compilation Strategy ============

///|
/// Configuration for tiered compilation
pub(all) struct TieredConfig {
  // Threshold for baseline compilation
  baseline_threshold : Int
  // Threshold for optimized recompilation
  optimize_threshold : Int
  // Whether to compile synchronously (blocking) or asynchronously
  synchronous : Bool
  // Maximum concurrent compilations (for async mode)
  max_concurrent : Int
}

///|
pub fn TieredConfig::default() -> TieredConfig {
  {
    baseline_threshold: 100,
    optimize_threshold: 10000,
    synchronous: true,
    max_concurrent: 1,
  }
}

///|
pub fn TieredConfig::eager() -> TieredConfig {
  // Compile early, optimize early
  {
    baseline_threshold: 10,
    optimize_threshold: 1000,
    synchronous: true,
    max_concurrent: 1,
  }
}

///|
pub fn TieredConfig::deferred() -> TieredConfig {
  // Compile late, less optimization
  {
    baseline_threshold: 1000,
    optimize_threshold: 100000,
    synchronous: true,
    max_concurrent: 1,
  }
}

// ============ Compilation Decision ============

///|
/// Decision about what to do with a function call
pub(all) enum CompilationDecision {
  // Interpret the function
  Interpret
  // Compile and then execute
  CompileAndExecute(CompilationMode)
  // Execute already compiled code
  ExecuteCompiled
}

///|
fn CompilationDecision::to_string(self : CompilationDecision) -> String {
  match self {
    Interpret => "interpret"
    CompileAndExecute(mode) => "compile_and_execute(\{mode})"
    ExecuteCompiled => "execute_compiled"
  }
}

///|
pub impl Show for CompilationDecision with fn output(self, logger) {
  logger.write_string(self.to_string())
}

// ============ Compilation Strategy ============

///|
/// The main compilation strategy manager
pub(all) struct CompilationStrategy {
  // Profiler for call counting
  profiler : Profiler
  // Compilation queue
  queue : CompilationQueue
  // Tiered configuration
  config : TieredConfig
  // Statistics
  mut decisions_interpret : Int
  mut decisions_compile : Int
  mut decisions_compiled : Int
}

///|
pub fn CompilationStrategy::CompilationStrategy(
  config : TieredConfig,
) -> CompilationStrategy {
  let threshold = HotThreshold::{
    warm_threshold: config.baseline_threshold / 2,
    hot_threshold: config.baseline_threshold,
  }
  {
    profiler: Profiler(threshold),
    queue: CompilationQueue(100), // Max 100 pending compilations
    config,
    decisions_interpret: 0,
    decisions_compile: 0,
    decisions_compiled: 0,
  }
}

///|
/// Make a compilation decision for a function call
pub fn CompilationStrategy::decide(
  self : CompilationStrategy,
  func_idx : Int,
) -> CompilationDecision {
  // Check if already compiled
  if self.profiler.is_compiled(func_idx) {
    self.decisions_compiled = self.decisions_compiled + 1
    return ExecuteCompiled
  }

  // Record the call and check if it should trigger compilation
  let (count, should_compile) = self.profiler.record_call(func_idx)
  if should_compile {
    // Determine compilation mode based on count
    let mode = if count >= self.config.optimize_threshold {
      Optimized
    } else {
      Baseline
    }
    self.decisions_compile = self.decisions_compile + 1
    return CompileAndExecute(mode)
  }
  self.decisions_interpret = self.decisions_interpret + 1
  Interpret
}

///|
/// Mark a function as compiled
pub fn CompilationStrategy::mark_compiled(
  self : CompilationStrategy,
  func_idx : Int,
) -> Unit {
  self.profiler.mark_compiled(func_idx)
}

///|
/// Check if a function is compiled
pub fn CompilationStrategy::is_compiled(
  self : CompilationStrategy,
  func_idx : Int,
) -> Bool {
  self.profiler.is_compiled(func_idx)
}

///|
/// Schedule a function for background compilation
pub fn CompilationStrategy::schedule_compilation(
  self : CompilationStrategy,
  func_idx : Int,
  mode : CompilationMode,
) -> Bool {
  let priority = self.profiler.counter.get_count(func_idx)
  let request = CompilationRequest::with_priority(func_idx, mode, priority)
  self.queue.enqueue(request)
}

///|
/// Get the next function to compile (for background compilation)
pub fn CompilationStrategy::next_to_compile(
  self : CompilationStrategy,
) -> CompilationRequest? {
  self.queue.dequeue()
}

///|
/// Get strategy statistics
pub fn CompilationStrategy::stats(
  self : CompilationStrategy,
) -> (Int, Int, Int, Int, Int, Int) {
  // (total_calls, interpreted, compiled_calls, compilations, queue_len, hot)
  let (total, _, hot, compiled) = self.profiler.stats()
  (
    total,
    self.decisions_interpret,
    self.decisions_compiled,
    compiled,
    self.queue.length(),
    hot,
  )
}

///|
fn CompilationStrategy::to_string(self : CompilationStrategy) -> String {
  let (total, interp, compiled_calls, compilations, queue, hot) = self.stats()
  "CompilationStrategy(calls=\{total}, interp=\{interp}, jit=\{compiled_calls}, compilations=\{compilations}, queue=\{queue}, hot=\{hot})"
}

///|
pub impl Show for CompilationStrategy with fn output(self, logger) {
  logger.write_string(self.to_string())
}