use argonauts_core::RunId;
use argonauts_core::events::session::CliWorkflowRun;
use argonauts_core::names::{SessionKey, WorkflowId};
use argonauts_core::views::{
PhaseCount, PhaseRow, RowStatus, WorkflowRow, WorkflowView, agents_counted,
};
use super::WorkflowLog;
pub(super) fn as_the_runs(
view: &mut WorkflowView,
log: &WorkflowLog,
run: &RunId,
) -> Option<String> {
let found = cli_workflows(&view.phases);
let (at, id, host, worker) = found.first()?.clone();
if found.iter().any(|(_, _, other, _)| *other != host) {
return None;
}
let later: Vec<WorkflowId> = found[1..].iter().map(|(_, id, ..)| id.clone()).collect();
let inner = log.agent_view(run, &id)?;
let name = inner.name.clone();
let mut phases = inner.phases;
if phases.is_empty() {
return None;
}
let mut asked = answered(&phases, log);
for later in &later {
if let Some(view) = log.agent_view(run, later) {
asked.extend(answered(&view.phases, log));
}
}
let alone = view.phases[at].rows.iter().all(|row| match row {
WorkflowRow::Agent(agent) => {
agent.session == host
|| log
.sessions
.session(&agent.session)
.is_some_and(|facts| facts.task.is_none())
}
_ => true,
});
if !alone {
return None;
}
let held = std::mem::take(&mut view.phases[at].rows);
let mut planner = None;
let mut rest = Vec::new();
let mut inside = Vec::new();
for row in held {
match row {
WorkflowRow::Agent(agent) if agent.session == host => planner = Some(agent),
WorkflowRow::Workflow(nested) if nested.id == id => {}
WorkflowRow::Workflow(mut nested) if later.contains(&nested.id) => {
nested.parent = None;
inside.push(WorkflowRow::Workflow(nested));
}
WorkflowRow::Agent(agent) if asked.contains(&agent.label) => {}
other => rest.push(other),
}
}
if let Some((_, workflow)) = log.cli_workflow(&id) {
for row in rest {
let at = placed(&phases, workflow, &row, log);
phases[at].rows.push(row);
}
}
if !inside.is_empty() {
let ran = phases
.iter()
.rposition(|phase| phase.status != RowStatus::Waiting)
.unwrap_or(phases.len() - 1);
let running: Vec<&str> = inside
.iter()
.filter_map(|row| match row {
WorkflowRow::Workflow(nested) if nested.status == RowStatus::Running => {
Some(nested.name.as_str())
}
_ => None,
})
.collect();
let waits = running
.last()
.map(|name| format!("Merge waits for {name}, then merges the change"));
phases[ran].rows.extend(inside);
if let Some(waits) = waits {
phases[ran].status = RowStatus::Running;
phases[ran].note = Some(waits);
}
}
for phase in &mut phases {
let (done, total) = counted(&phase.rows);
phase.count = PhaseCount::of(done, total);
}
let counts: (u32, u32) = phases
.iter()
.map(|phase| counted(&phase.rows))
.fold((0, 0), |(done, total), (d, t)| (done + d, total + t));
let no_agents = phases.iter().all(|phase| {
!phase
.rows
.iter()
.any(|row| matches!(row, WorkflowRow::Agent(_)))
});
if let Some(planner) = planner.filter(|_| no_agents) {
phases[0].rows.insert(0, WorkflowRow::Agent(planner));
}
view.phases = phases;
(view.agents_done, view.agents_total) = counts;
view.worker = worker;
Some(name)
}
fn placed(
phases: &[PhaseRow],
workflow: &CliWorkflowRun,
row: &WorkflowRow,
log: &WorkflowLog,
) -> usize {
let started = match row {
WorkflowRow::Agent(agent) => log
.sessions
.session(&agent.session)
.and_then(|facts| super::epoch_ms(facts.started_at)),
_ => None,
};
let Some(started) = started else {
return 0;
};
let asker = workflow
.agents
.iter()
.filter(|agent| agent.started_at_ms.is_some_and(|at| at <= started))
.max_by_key(|agent| agent.started_at_ms);
let title = asker.and_then(|agent| {
let phase = agent.phase?;
workflow.phases.iter().find(|known| known.index == phase)
});
title
.and_then(|title| {
let name = argonauts_core::text::shown_name(&title.title);
phases.iter().position(|phase| phase.name == name)
})
.unwrap_or(0)
}
fn answered(phases: &[PhaseRow], log: &WorkflowLog) -> Vec<String> {
phases
.iter()
.flat_map(|phase| &phase.rows)
.filter_map(|row| match row {
WorkflowRow::Agent(agent) if agent.answered => log
.sessions
.session(&agent.session)
.map(|session| session.label()),
_ => None,
})
.collect()
}
fn cli_workflows(phases: &[PhaseRow]) -> Vec<(usize, WorkflowId, SessionKey, Option<String>)> {
phases
.iter()
.enumerate()
.flat_map(|(at, phase)| {
phase.rows.iter().filter_map(move |row| match row {
WorkflowRow::Workflow(nested) => {
let host = nested.parent.clone()?;
Some((at, nested.id.clone(), host, nested.worker.clone()))
}
_ => None,
})
})
.collect()
}
fn counted(rows: &[WorkflowRow]) -> (u32, u32) {
agents_counted(rows, |_| false)
}