///|
/// Global trace state
priv struct TraceState {
  mut stdout_enabled : Bool
  mut chrome_path : String?
  mut events : Array[Json]
  mut start : Instant?
}

///|
let state : TraceState = {
  stdout_enabled: false,
  chrome_path: None,
  events: [],
  start: None,
}

///|
/// Initialize the tracing system.
/// - `stdout`: if true, log messages are printed to stdout
/// - `chrome`: if Some(path), Chrome trace events are written to the file
pub fn setup(stdout? : Bool = false, chrome? : String? = None) -> Unit {
  state.stdout_enabled = stdout
  state.chrome_path = chrome
  if chrome is Some(_) {
    state.start = Some(instant_now())
    state.events = []
  }
}

///|
/// Log a debug message.
pub fn debug(msg : String) -> Unit {
  if state.stdout_enabled {
    println("[DEBUG] \{msg}")
  }
  write_instant("debug: \{msg}")
}

///|
/// Log an info message.
pub fn info(msg : String) -> Unit {
  if state.stdout_enabled {
    println("[INFO] \{msg}")
  }
  write_instant("info: \{msg}")
}

///|
/// Execute a sync function and record its duration as a Chrome trace event.
pub fn[T] scope(name : String, f : () -> T raise?) -> T raise? {
  let start = instant_now()
  let result = f()
  let end = instant_now()
  write_complete(name, 0, start, end)
  result
}

///|
/// Execute an async function and record its duration as a Chrome trace event.
pub async fn[T] scope_async(name : String, f : async () -> T) -> T {
  let start = instant_now()
  let result = f()
  let end = instant_now()
  write_complete(name, 0, start, end)
  result
}

///|
/// Finalize and write the trace file.
pub async fn close() -> Unit {
  match state.chrome_path {
    Some(path) => {
      let json = state.events.to_json()
      let file = @fs.create(path, permission=0o644)
      file.write(json.stringify())
      file.close()
    }
    None => ()
  }
}

///|
/// Write an instant event (ph:"i") for logging.
fn write_instant(name : String) -> Unit {
  guard state.start is Some(_) else { return }
  let ts = elapsed_micros(state.start.unwrap())
  let event : Json = { "pid": 0, "name": name, "ts": ts, "ph": "i", "s": "g" }
  state.events.push(event)
}

///|
/// Write a complete/duration event (ph:"X") for scope timing.
fn write_complete(
  name : String,
  tid : Int,
  start : Instant,
  end : Instant,
) -> Unit {
  guard state.start is Some(trace_start) else { return }
  let ts = elapsed_micros_from(trace_start, start)
  let dur = elapsed_micros_between(start, end)
  let event : Json = {
    "pid": 0,
    "name": name,
    "ts": ts,
    "tid": tid,
    "ph": "X",
    "dur": dur,
  }
  state.events.push(event)
}

///|
/// Calculate elapsed microseconds from trace start to now.
fn elapsed_micros(start : Instant) -> Double {
  (instant_as_secs_f64(instant_now()) - instant_as_secs_f64(start)) *
  1_000_000.0
}

///|
/// Calculate elapsed microseconds from trace_start to a specific instant.
fn elapsed_micros_from(trace_start : Instant, instant : Instant) -> Double {
  (instant_as_secs_f64(instant) - instant_as_secs_f64(trace_start)) *
  1_000_000.0
}

///|
/// Calculate elapsed microseconds between two instants.
fn elapsed_micros_between(start : Instant, end : Instant) -> Double {
  (instant_as_secs_f64(end) - instant_as_secs_f64(start)) * 1_000_000.0
}