///|
pub(all) struct RunHistoryInput {
  bundles : Array[@workflow.AnalysisRunBundle]
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct RunHistoryEntry {
  run_id : @domain.RunId
  status : @workflow.AnalysisRunStatus
  symbol : @domain.Symbol
  timeframe : @domain.Timeframe
  adapter : String
  model : String
  review_count : Int
  blocking_review_count : Int
  replay_available : Bool
  export_item_count : Int
  safety_ok : Bool
  chart_path : String?
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct RunHistoryIndex {
  total_count : Int
  completed_count : Int
  awaiting_market_data_count : Int
  blocking_review_count : Int
  replayable_count : Int
  safety_issue_count : Int
  entries : Array[RunHistoryEntry]
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn run_history_input(
  bundles : Array[@workflow.AnalysisRunBundle],
) -> RunHistoryInput {
  { bundles, }
}

///|
fn run_history_entry(bundle : @workflow.AnalysisRunBundle) -> RunHistoryEntry {
  let view = @app.operator_run_view(bundle)
  let safety_report = @safety.prepare_safety_report(bundle)
  {
    run_id: view.run_id,
    status: view.status,
    symbol: view.symbol,
    timeframe: view.timeframe,
    adapter: view.adapter,
    model: view.model,
    review_count: view.review_count,
    blocking_review_count: view.blocking_review_count,
    replay_available: view.replay_available,
    export_item_count: view.export_item_count,
    safety_ok: safety_report.ok,
    chart_path: view.chart_path,
  }
}

///|
fn completed_count(entries : Array[RunHistoryEntry]) -> Int {
  entries.fold(init=0, fn(count, entry) {
    if entry.status is Completed {
      count + 1
    } else {
      count
    }
  })
}

///|
fn awaiting_market_data_count(entries : Array[RunHistoryEntry]) -> Int {
  entries.fold(init=0, fn(count, entry) {
    if entry.status is AwaitingMarketData {
      count + 1
    } else {
      count
    }
  })
}

///|
fn blocking_review_count(entries : Array[RunHistoryEntry]) -> Int {
  entries.fold(init=0, fn(count, entry) { count + entry.blocking_review_count })
}

///|
fn replayable_count(entries : Array[RunHistoryEntry]) -> Int {
  entries.fold(init=0, fn(count, entry) {
    if entry.replay_available {
      count + 1
    } else {
      count
    }
  })
}

///|
fn safety_issue_count(entries : Array[RunHistoryEntry]) -> Int {
  entries.fold(init=0, fn(count, entry) {
    if entry.safety_ok {
      count
    } else {
      count + 1
    }
  })
}

///|
pub fn render_run_history(input : RunHistoryInput) -> RunHistoryIndex {
  let entries = input.bundles.map(fn(bundle) { run_history_entry(bundle) })
  {
    total_count: entries.length(),
    completed_count: completed_count(entries),
    awaiting_market_data_count: awaiting_market_data_count(entries),
    blocking_review_count: blocking_review_count(entries),
    replayable_count: replayable_count(entries),
    safety_issue_count: safety_issue_count(entries),
    entries,
  }
}