use std::collections::{BTreeMap, BTreeSet};
use yog_data::Ref;
use yog_data::schema::Found;
use crate::content::refs::{self, RefKind};
type PlacedRef = (String, Option<BTreeSet<String>>);
pub(super) fn recipe_gaps(
items: &[Found<Ref>],
tags: &[Found<Ref>],
) -> Vec<(BTreeSet<String>, String)> {
let mut lists: BTreeMap<String, Vec<PlacedRef>> = BTreeMap::new();
for f in items {
if let Some(on) = item_hosts(f) {
lists
.entry(list_of(&f.path))
.or_default()
.push((f.value.id.clone(), on));
}
}
for f in tags {
let Some(on) = refs::tag_hosts(yog_ids::registry_of(&f.value.kind), &f.value.id) else {
continue;
};
let shown = format!("{}{}", yog_ids::sep::TAG_SHOWN, f.value.id);
lists.entry(list_of(&f.path)).or_default().push((shown, on));
}
lists.values().filter_map(|alts| gap(alts)).collect()
}
fn list_of(p: &yog_data::Path) -> String {
let mut p = p.clone();
if matches!(p.segments().last(), Some(yog_data::Seg::Index(_))) {
p.pop();
}
p.to_string()
}
fn item_hosts(f: &Found<Ref>) -> Option<Option<BTreeSet<String>>> {
let kind = RefKind::from_word(&f.value.kind)?;
let id = &f.value.id;
let potion_alias = kind == RefKind::Item && id.starts_with(yog_ids::yog::POTION_ALIAS);
if !potion_alias && yog_ids::game_path(id).is_none() {
return None;
}
let on: BTreeSet<String> = refs::hosts()
.iter()
.filter(|h| {
yog_formats::tags::host_version(h).is_some_and(|v| {
if potion_alias {
refs::is_vanilla_potion_in(id, v)
} else {
refs::equivalent(kind, id, v).is_some()
}
})
})
.cloned()
.collect();
Some((on.len() < refs::hosts().len()).then_some(on))
}
fn gap(alts: &[PlacedRef]) -> Option<(BTreeSet<String>, String)> {
if alts.iter().any(|(_, on)| on.is_none()) {
return None;
}
let on: BTreeSet<String> = alts
.iter()
.flat_map(|(_, on)| on.iter().flatten().cloned())
.collect();
let missing: BTreeSet<String> = refs::hosts()
.iter()
.filter(|h| !on.contains(*h))
.cloned()
.collect();
if missing.is_empty() {
return None;
}
let names: Vec<&str> = alts.iter().map(|(id, _)| id.as_str()).collect();
let item = yog_ids::kind::ITEM;
let hint = alts
.iter()
.filter_map(|(id, _)| {
id.strip_prefix(yog_ids::sep::TAG_SHOWN)
.and_then(|t| refs::yog_tag_for(item, t))
})
.find(|y| refs::tag_hosts(item, y) == Some(None))
.map(|y| {
format!(
" — {}{item}:{y} is on every version and loader",
yog_ids::reference::TAG
)
})
.unwrap_or_default();
Some((missing, format!("{} isn't there{hint}", names.join(" / "))))
}