use anyhow::Result;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
use crate::models::action::ActionModel;
use crate::models::flow::FlowModel;
pub struct TopologicalSort;
impl TopologicalSort {
pub fn sort(flow: &FlowModel) -> Result<Vec<ActionModel>> {
let mut tag_to_action: HashMap<&str, ActionModel> = HashMap::new();
for action in &flow.actions {
tag_to_action.insert(&action.tag, action.clone());
}
let mut in_degree: HashMap<&str, usize> = HashMap::new();
let mut deps: HashMap<&str, Vec<&str>> = HashMap::new();
for action in &flow.actions {
in_degree.entry(&action.tag).or_insert(0);
deps.entry(&action.tag).or_default();
let mut unique_deps: HashSet<String> = HashSet::new();
for action_text in action.actions() {
for mat in crate::engine::parser::TagIterator::new(action_text) {
if mat.base_tag != action.tag {
unique_deps.insert(mat.base_tag);
}
}
}
for dep_tag in unique_deps {
if let Some((&stable_tag, _)) = tag_to_action.get_key_value(dep_tag.as_str()) {
*in_degree.entry(&action.tag).or_insert(0) += 1;
deps.entry(stable_tag).or_default().push(&action.tag);
}
}
}
let mut queue: VecDeque<&str> = in_degree
.iter()
.filter(|(_, deg)| **deg == 0)
.map(|(tag, _)| *tag)
.collect();
let mut order: Vec<ActionModel> = Vec::with_capacity(flow.actions.len());
while let Some(tag) = queue.pop_front() {
if let Some(action) = tag_to_action.get(tag) {
order.push(action.clone());
}
if let Some(dependents) = deps.get(tag) {
for dep in dependents {
if let Some(deg) = in_degree.get_mut(dep) {
*deg -= 1;
if *deg == 0 {
queue.push_back(dep);
}
}
}
}
}
if order.len() != flow.actions.len() {
anyhow::bail!(
"Circular dependency detected in actions. Expected {} steps, resolved {}.",
flow.actions.len(),
order.len()
);
}
Ok(order)
}
}