///|
/// Benchmark: Input set and get operations
test "bench: Input set/get" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  // Pre-populate
  for i in 0..<1000 {
    input.set(rt, i, i * 2) |> ignore
  }
  b.bench(name="Input::get (1000 items)", fn() {
    for i in 0..<1000 {
      input.get(rt, i) |> ignore
    }
  })
}

///|
/// Benchmark: Query fetch with cache hit
test "bench: Query cache hit" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  input.set(rt, 0, 42) |> ignore
  let query : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key).unwrap_or(0) * 2
  })
  // Warm up cache
  query.fetch(rt, 0) |> ignore
  b.bench(name="Query::fetch cache hit", fn() {
    for _ in 0..<1000 {
      query.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Query fetch with cache miss (recompute)
test "bench: Query recompute" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  let query : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key).unwrap_or(0) * 2
  })
  b.bench(name="Query::fetch recompute", fn() {
    for i in 0..<100 {
      input.set(rt, 0, i) |> ignore
      query.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Chained queries
test "bench: Chained queries" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  // Query chain: q1 -> q2 -> q3
  let q1 : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key).unwrap_or(0) + 1
  })
  let q2 : Query[Int, Int] = Query::new(2, fn(rt, key) { q1.fetch(rt, key) * 2 })
  let q3 : Query[Int, Int] = Query::new(3, fn(rt, key) {
    q2.fetch(rt, key) + 10
  })
  input.set(rt, 0, 5) |> ignore
  // Warm up
  q3.fetch(rt, 0) |> ignore
  b.bench(name="Chained queries (3 levels) cache hit", fn() {
    for _ in 0..<1000 {
      q3.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Many keys
test "bench: Many keys" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  let query : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key).unwrap_or(0) * 2
  })
  // Pre-populate
  for i in 0..<1000 {
    input.set(rt, i, i) |> ignore
    query.fetch(rt, i) |> ignore
  }
  b.bench(name="Query::fetch 1000 different keys (cached)", fn() {
    for i in 0..<1000 {
      query.fetch(rt, i) |> ignore
    }
  })
}

///|
/// Benchmark: Backdate optimization
test "bench: Backdate" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  // Query returns constant regardless of input
  let query : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key) |> ignore
    42 // Always returns 42
  })
  input.set(rt, 0, 1) |> ignore
  query.fetch(rt, 0) |> ignore
  b.bench(name="Backdate (same result recompute)", fn() {
    for i in 0..<100 {
      input.set(rt, 0, i) |> ignore
      query.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Revision operations
test "bench: Revision" (b : @bench.T) {
  b.bench(name="Revision::next 10000 times", fn() {
    let mut r = Revision::zero()
    for _ in 0..<10000 {
      r = r.next()
    }
    b.keep(r)
  })
}

///|
/// Benchmark: Runtime dependency tracking
test "bench: Dependency tracking" (b : @bench.T) {
  let rt = Runtime::new()
  b.bench(name="Record 100 dependencies", fn() {
    rt.push_query(0, 0)
    for i in 0..<100 {
      rt.record_dependency(i, i, Revision::new(i), Durability::Low) |> ignore
    }
    rt.pop_query() |> ignore
  })
}

///|
/// Benchmark: Durability optimization
test "bench: Durability" (b : @bench.T) {
  let rt = Runtime::new()
  // High durability input (rarely changes)
  let stdlib : Input[Int, Int] = Input::new_with_durability(0, Durability::High)
  // Low durability input (frequently changes)
  let user_file : Input[Int, Int] = Input::new_with_durability(
    1,
    Durability::Low,
  )
  // Query depends on high durability input only
  let stdlib_query : Query[Int, Int] = Query::new(2, fn(rt, key) {
    stdlib.get(rt, key).unwrap_or(0) * 2
  })
  // Set high durability input once
  stdlib.set(rt, 0, 100) |> ignore
  stdlib_query.fetch(rt, 0) |> ignore
  b.bench(name="High durability skip (100 low changes)", fn() {
    for i in 0..<100 {
      // Only change low durability input
      user_file.set(rt, 0, i) |> ignore
      // Query should skip recompute (depends only on high durability)
      stdlib_query.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Deep verify with backdate optimization
/// When intermediate query returns same value, downstream skips recompute
test "bench: Deep verify backdate" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  input.register(rt)
  // Query 1: returns constant (always backdates)
  let q1 : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key) |> ignore
    42 // Always returns 42
  })
  q1.register(rt)
  // Query 2: depends on q1
  let q2 : Query[Int, Int] = Query::new(2, fn(rt, key) { q1.fetch(rt, key) * 2 })
  // Warm up
  input.set(rt, 0, 1) |> ignore
  q2.fetch(rt, 0) |> ignore
  b.bench(name="Deep verify backdate (100 changes)", fn() {
    for i in 0..<100 {
      input.set(rt, 0, i) |> ignore
      // q1 recomputes, q2 should skip (deep verify + backdate)
      q2.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Deep verify chain propagation
/// Three-level chain where abs() causes backdate on sign change
test "bench: Deep verify chain" (b : @bench.T) {
  let rt = Runtime::new()
  let input : Input[Int, Int] = Input::new(0)
  input.register(rt)
  // q1: abs - backdates when sign changes
  let q1 : Query[Int, Int] = Query::new(1, fn(rt, key) {
    input.get(rt, key).unwrap_or(0).abs()
  })
  q1.register(rt)
  // q2: depends on q1
  let q2 : Query[Int, Int] = Query::new(2, fn(rt, key) {
    q1.fetch(rt, key) * 10
  })
  q2.register(rt)
  // q3: depends on q2
  let q3 : Query[Int, Int] = Query::new(3, fn(rt, key) { q2.fetch(rt, key) + 1 })
  // Warm up
  input.set(rt, 0, 5) |> ignore
  q3.fetch(rt, 0) |> ignore
  b.bench(name="Deep verify chain (50 positive, 50 negative)", fn() {
    for i in 0..<50 {
      // Alternating between 5 and -5
      input.set(rt, 0, if i % 2 == 0 { 5 } else { -5 }) |> ignore
      q3.fetch(rt, 0) |> ignore
    }
  })
}

///|
/// Benchmark: Intern new values
test "bench: Intern new values" (b : @bench.T) {
  let rt = Runtime::new()
  let strings : Intern[String] = Intern::new(0)
  b.bench(name="Intern 1000 unique strings", fn() {
    for i in 0..<1000 {
      strings.intern(rt, "string_" + i.to_string()) |> ignore
    }
  })
}

///|
/// Benchmark: Intern lookup (existing values)
test "bench: Intern lookup" (b : @bench.T) {
  let rt = Runtime::new()
  let strings : Intern[String] = Intern::new(0)
  // Pre-populate
  for i in 0..<1000 {
    strings.intern(rt, "string_" + i.to_string()) |> ignore
  }
  b.bench(name="Intern lookup 1000 existing", fn() {
    for i in 0..<1000 {
      strings.intern(rt, "string_" + i.to_string()) |> ignore
    }
  })
}

///|
/// Benchmark: Intern ID comparison
test "bench: InternId comparison" (b : @bench.T) {
  let rt = Runtime::new()
  let strings : Intern[String] = Intern::new(0)
  let id1 = strings.intern(rt, "hello")
  let id2 = strings.intern(rt, "world")
  b.bench(name="InternId comparison 10000", fn() {
    for _ in 0..<10000 {
      let _ = id1 == id2
      ()
    }
    b.keep(id1)
  })
}