use crate::config::Config;
use crate::ticket::Ticket;
use std::collections::HashSet;
pub fn verify_tickets(
config: &Config,
tickets: &[Ticket],
merged: &HashSet<String>,
) -> Vec<String> {
let valid_states: HashSet<&str> = config.workflow.states.iter()
.map(|s| s.id.as_str())
.collect();
let terminal = config.terminal_state_ids();
let in_progress_states: HashSet<&str> =
["in_progress", "implemented"].iter().copied().collect();
let mut issues: Vec<String> = Vec::new();
for t in tickets {
let fm = &t.frontmatter;
if terminal.contains(fm.state.as_str()) { continue; }
let prefix = format!("#{} [{}]", fm.id, fm.state);
if !valid_states.is_empty() && !valid_states.contains(fm.state.as_str()) {
issues.push(format!("{prefix}: unknown state {:?}", fm.state));
}
if let Some(name) = t.path.file_name().and_then(|n| n.to_str()) {
let expected_prefix = format!("{:04}", fm.id);
if !name.starts_with(&expected_prefix) {
issues.push(format!("{prefix}: id {} does not match filename {name}", fm.id));
}
}
if in_progress_states.contains(fm.state.as_str()) && fm.branch.is_none() {
issues.push(format!("{prefix}: state requires branch but none set"));
}
if let Some(branch) = &fm.branch {
if (fm.state == "in_progress" || fm.state == "implemented")
&& merged.contains(branch.as_str())
{
issues.push(format!("{prefix}: branch {branch} is merged but ticket not closed"));
}
}
if !t.body.contains("## Spec") {
issues.push(format!("{prefix}: missing ## Spec section"));
}
if !t.body.contains("## History") {
issues.push(format!("{prefix}: missing ## History section"));
}
if let Ok(doc) = t.document() {
for err in doc.validate(&config.ticket.sections) {
issues.push(format!("{prefix}: {err}"));
}
}
}
issues
}