use super::*;
const REPAIR_WINDOW_MS: i64 = 5_000;
pub(crate) struct Walked {
pub(crate) floor: usize,
pub(crate) boundary: Option<usize>,
}
pub(crate) fn run(b: &mut Builder<'_>, leaf: Option<usize>) -> Walked {
let mut out = Walked {
floor: leaf.unwrap_or(0),
boundary: None,
};
let Some(leaf) = leaf else {
out.floor = b.len();
return out;
};
let mut crossed = false;
let mut cur = Some(leaf);
while let Some(i) = cur {
if b.visited[i] {
break; }
b.visited[i] = true;
b.on_chain[i] = true;
b.precut[i] = crossed;
out.floor = i;
let Some(pu) = b.parent[i] else {
if b.is_boundary(i) {
out.boundary.get_or_insert(i);
}
break;
};
b.chain_child.entry(pu).or_insert(i);
let next = match b.resolve(pu) {
Some(j) if !b.visited[j] => Some(j),
_ => repair(b, i),
};
let next = match next {
Some(j) => Some(j),
None if b.is_boundary(i) => {
out.boundary.get_or_insert(i);
crossed = true;
b.recs[i]
.logical_parent_uuid()
.and_then(|u| b.resolve(u))
.filter(|&j| !b.visited[j])
}
None => None,
};
cur = next;
}
rescue_message_id(b);
rescue_leaf_descendants(b, leaf);
out
}
fn repair(b: &mut Builder<'_>, i: usize) -> Option<usize> {
let now = b.recs[i].timestamp().and_then(crate::timez::epoch_ms)?;
let side = b.recs[i].is_sidechain();
if b.ts_sorted.is_none() {
let mut v: Vec<(i64, usize)> = (0..b.len())
.filter(|&k| b.admit[k] && !b.removed[k] && b.is_survivor(k))
.filter_map(|k| {
b.recs[k]
.timestamp()
.and_then(crate::timez::epoch_ms)
.map(|ms| (ms, k))
})
.collect();
v.sort_unstable();
b.ts_sorted = Some(v);
}
let table = b.ts_sorted.as_ref()?;
let mut at = table.partition_point(|&(ms, _)| ms <= now);
while at > 0 {
at -= 1;
let (ms, k) = table[at];
if now - ms > REPAIR_WINDOW_MS {
return None;
}
if !b.visited[k] && b.recs[k].is_sidechain() == side {
return Some(k);
}
}
None
}
fn rescue_message_id(b: &mut Builder<'_>) {
let ids: HashSet<&str> = (0..b.len())
.filter(|&i| b.on_chain[i] && b.recs[i].kind() == Some("assistant"))
.filter_map(|i| b.recs[i].message_id())
.collect();
if ids.is_empty() {
return;
}
let siblings: Vec<usize> = (0..b.len())
.filter(|&i| {
!b.visited[i]
&& b.admit[i]
&& !b.removed[i]
&& b.is_survivor(i)
&& b.recs[i].kind() == Some("assistant")
&& b.recs[i].message_id().is_some_and(|id| ids.contains(id))
})
.collect();
let mut group: HashSet<&str> = (0..b.len())
.filter(|&i| b.on_chain[i] && b.recs[i].kind() == Some("assistant"))
.filter_map(|i| b.uuid(i))
.collect();
for i in siblings {
b.visited[i] = true;
b.on_chain[i] = true;
if let Some(u) = b.uuid(i) {
group.insert(u);
}
}
let carriers: Vec<usize> = (0..b.len())
.filter(|&i| {
!b.visited[i]
&& b.admit[i]
&& !b.removed[i]
&& b.is_survivor(i)
&& b.recs[i].kind() == Some("user")
&& b.parent[i].is_some_and(|p| group.contains(p))
&& b.recs[i].has_tool_result()
})
.collect();
for i in carriers {
b.visited[i] = true;
b.on_chain[i] = true;
}
}
fn rescue_leaf_descendants<'a>(b: &mut Builder<'a>, leaf: usize) {
let Some(root) = b.uuid(leaf) else {
return;
};
let mut kids: HashMap<&'a str, Vec<usize>> = HashMap::new();
for i in 0..b.len() {
if !b.admit[i] || b.removed[i] || !b.is_survivor(i) || b.is_conv(i) {
continue;
}
if let Some(p) = b.parent[i] {
kids.entry(p).or_default().push(i);
}
}
let mut queue: Vec<&'a str> = vec![root];
while let Some(u) = queue.pop() {
let Some(children) = kids.get(u) else {
continue;
};
let children = children.clone();
for i in children {
if b.visited[i] {
continue;
}
b.visited[i] = true;
b.on_chain[i] = true;
if let Some(cu) = b.uuid(i) {
queue.push(cu);
}
}
}
}