use crate::jev::Answers;
use crate::{config, output, repair, rules, schema};
use std::path::Path;
pub fn run(cmd_dir: &Path, typed: &[String]) -> i32 {
if let Some(line) = line(cmd_dir, typed) {
output::stdout(&format!("{line}\n"));
}
0
}
fn line(cmd_dir: &Path, typed: &[String]) -> Option<String> {
if typed.iter().any(|w| w == "--") {
return None;
}
let words: Vec<String> = typed.iter().filter(|w| !w.starts_with("--")).cloned().collect();
let cfg = config::load().ok();
if cfg.as_ref().is_some_and(|c| !c.suggest.enabled) {
return None;
}
let (schema, used) = schema::resolve(cmd_dir, &words).ok()?;
if schema.placeholders.is_empty() {
return None;
}
let aliases = cfg.and_then(|c| c.cmd.get(&schema.command.name).map(|c| c.aliases.clone())).unwrap_or_default();
let rest = config::expand_aliases(&aliases, &words[used..]);
let mut tokens = rules::classify(&schema, &rest);
repair::repair(&schema, &mut tokens, &Answers::new());
let slots = &schema.placeholders;
let mut filled: Vec<bool> = slots.iter().map(|p| tokens.iter().any(|t| p.roles.iter().any(|r| t.is(r)))).collect();
let has_amount = |p: &schema::Placeholder, dim: Option<&str>| {
p.roles.iter().any(|r| schema.role(r).and_then(|r| r.amount.as_deref()).is_some_and(|d| dim.is_none_or(|want| d == want)))
};
for t in tokens.iter().filter(|t| !t.resolved()) {
let dim = t.amount.as_ref().and_then(|a| a.unit.as_deref()).and_then(|u| schema.unit_dimension(u));
let open = |i: usize, p: &schema::Placeholder| !filled[i] && if t.amount.is_some() { has_amount(p, dim.as_deref()) } else { p.bare };
if let Some(i) = slots.iter().enumerate().position(|(i, p)| open(i, p)) {
filled[i] = true;
}
}
let left: Vec<&str> = slots.iter().zip(&filled).filter(|(_, f)| !**f).map(|(p, _)| p.text.as_str()).collect();
(!left.is_empty()).then(|| left.join(" "))
}