///|
pub(all) enum BenchmarkShape {
  Chain
  Mixed
  Fanout
} derive(Eq, @debug.Debug)

///|
pub struct BenchmarkSpec {
  name : String
  task_count : Int
  depth : Int
  fanout : Int
  shape : BenchmarkShape
} derive(Eq, @debug.Debug)

///|
pub fn benchmark_specs() -> Array[BenchmarkSpec] {
  [
    { name: "small-chain", task_count: 12, depth: 11, fanout: 1, shape: Chain },
    { name: "medium-mixed", task_count: 48, depth: 43, fanout: 6, shape: Mixed },
    { name: "wide-fanout", task_count: 33, depth: 1, fanout: 32, shape: Fanout },
    { name: "deep-chain", task_count: 40, depth: 39, fanout: 1, shape: Chain },
  ]
}

///|
pub fn build_benchmark_project(spec : BenchmarkSpec) -> Project {
  let tasks : Map[String, Task] = Map([])
  let non_build_count = spec.task_count - 1
  match spec.shape {
    Chain => add_chain_tasks(tasks, non_build_count)
    Fanout => add_fanout_tasks(tasks, non_build_count)
    Mixed => add_mixed_tasks(tasks, non_build_count, spec.fanout)
  }
  let build_deps = match spec.shape {
    Chain => [benchmark_task_name(non_build_count - 1)]
    Fanout => all_leaf_names(non_build_count)
    Mixed => {
      let deps = all_leaf_names(spec.fanout)
      deps.push(benchmark_task_name(non_build_count - 1))
      deps
    }
  }
  tasks["build"] = benchmark_task(
    name="build",
    deps=build_deps,
    output="benchmarks/out/build.txt",
  )
  {
    root: ".",
    config_path: "benchmarks/generated/\{spec.name}/Moonforge.toml",
    tasks,
  }
}

///|
pub fn benchmark_description(spec : BenchmarkSpec) -> String {
  let shape = match spec.shape {
    Chain => "chain"
    Mixed => "mixed fanout and chain"
    Fanout => "wide fanout"
  }
  "\{spec.name}: \{spec.task_count} tasks, depth \{spec.depth}, \{shape}"
}

///|
fn add_chain_tasks(tasks : Map[String, Task], count : Int) -> Unit {
  for i in 0.. Unit {
  for i in 0.. Unit {
  for i in 0.. Task {
  {
    name,
    cmd: "python -c \"print('moonforge benchmark: \{name}')\"",
    deps,
    inputs: [],
    outputs: [output],
    phony: false,
    desc: Some("generated benchmark workload task"),
  }
}

///|
fn benchmark_task_name(index : Int) -> String {
  "step-\{three_digit(index)}"
}

///|
fn leaf_task_name(index : Int) -> String {
  "leaf-\{three_digit(index)}"
}

///|
fn three_digit(value : Int) -> String {
  if value < 10 {
    "00\{value}"
  } else if value < 100 {
    "0\{value}"
  } else {
    value.to_string()
  }
}

///|
fn all_leaf_names(count : Int) -> Array[String] {
  let names = []
  for i in 0..