// examples/build_pipeline — runnable demonstration of the `src/build_tool`
// direction as a realistic incremental build: parse a makefile-style rule
// set for a multi-module C project, schedule parallel waves, propagate a
// single-header change into a minimal rebuild set, execute actions with an
// up-to-date cache, and bisect a regression range.
//
// Run with:
//   moon run examples/build_pipeline

///|
fn main {
  println("=== 1. parse rules -> dependency graph ===")
  let rules_text = @build.demo_rules()
  println(rules_text)
  let graph = match @build.parse_rules(rules_text) {
    Ok(g) => g
    Err(e) => {
      println("parse error at line \{e.line}: \{e.message}")
      return
    }
  }
  match @build.detect_cycle(graph) {
    Some(cycle) =>
      println(
        "cycle detected: \{cycle.map(fn(t) { t.to_string() }).join(" -> ")}",
      )
    None =>
      println(
        "acyclic: \{graph.nodes.length()} targets, \{graph.edges.length()} edges",
      )
  }
  println("critical path length: \{@build.critical_path_length(graph)}")
  println("")
  println("=== 2. parallel wave schedule (2 jobs) ===")
  let waves = @build.schedule(graph, 2)
  let mut wave_no = 0
  for wave in waves {
    wave_no = wave_no + 1
    println("wave \{wave_no}: \{wave.map(fn(t) { t.to_string() }).join(", ")}")
  }
  println("")
  println("=== 3. incremental rebuild: touch common.h ===")
  let dirty = @build.propagate_dirty(graph, [@build.Target::new("common.h")])
  let minimal = @build.minimal_rebuild_set(graph, [
    @build.Target::new("common.h"),
  ])
  println("dirty closure : \{dirty.map(fn(t) { t.to_string() }).join(", ")}")
  println(
    "minimal set   : \{minimal.length()} of \{graph.nodes.length()} targets rebuilt",
  )
  println("")
  println("=== 4. cached action execution: second run is a no-op ===")
  let actions : Array[@build.Action] = [
    {
      id: "cc_core",
      inputs: ["core_a.c", "common.h"],
      outputs: ["core_a.o"],
      command: "cc -c core_a.c",
    },
    {
      id: "link_app",
      inputs: ["core_a.o"],
      outputs: ["app"],
      command: "cc -o app core_a.o",
    },
  ]
  let sched = @build.build_parallel_schedule(actions, [
    ("core_a.c", "h1"),
    ("common.h", "h2"),
  ])
  let executed : Array[String] = []
  let exec = @build.CallbackExecutor::new(fn(a) {
    executed.push(a.id)
    "out-\{a.id}"
  })
  let log1 = @build.run_actions(sched, exec, @build.BuildLog::new())
  println("first run executed : \{executed.join(", ")}")
  executed.clear()
  let _log2 = @build.run_actions(sched, exec, log1)
  println("second run executed: [\{executed.join(", ")}] (cache hit, skipped)")
  println("")
  println("=== 5. auto-bisect a regression range ===")
  let report = @build.auto_bisect(32, fn(commit) {
    if commit >= 21 {
      @build.BisectVerdict::BisectBad
    } else {
      @build.BisectVerdict::BisectGood
    }
  })
  match report.first_bad {
    Some(bad) =>
      println("first bad commit: \{bad} (probes: \{report.probes.length()})")
    None => println("no bad commit found (probes: \{report.probes.length()})")
  }
}