use crate::model::Model;
use std::collections::HashSet;

/// A flattened entry in the category panel tree.
#[derive(Debug, Clone)]
pub enum CatTreeEntry {
    /// Category header row: name, item count, expanded?
    Category {
        name: String,
        item_count: usize,
        expanded: bool,
    },
    /// Item row under a category
    Item { cat_name: String, item_name: String },
}

impl CatTreeEntry {
    /// The category this entry belongs to.
    pub fn cat_name(&self) -> &str {
        match self {
            CatTreeEntry::Category { name, .. } => name,
            CatTreeEntry::Item { cat_name, .. } => cat_name,
        }
    }
}

/// Build the flattened tree of categories and their items.
pub fn build_cat_tree(model: &Model, expanded: &HashSet<String>) -> Vec<CatTreeEntry> {
    let mut entries = Vec::new();
    for cat_name in model.category_names() {
        let cat = model.category(cat_name);
        let item_count = cat.map(|c| c.items.len()).unwrap_or(0);
        let is_expanded = expanded.contains(cat_name);
        entries.push(CatTreeEntry::Category {
            name: cat_name.to_string(),
            item_count,
            expanded: is_expanded,
        });
        if is_expanded && let Some(cat) = cat {
            for item_name in cat.ordered_item_names() {
                entries.push(CatTreeEntry::Item {
                    cat_name: cat_name.to_string(),
                    item_name: item_name.to_string(),
                });
            }
        }
    }
    entries
}

#[cfg(test)]
mod tests {
    use super::*;

    fn make_model_with_categories(cats: &[(&str, &[&str])]) -> Model {
        let mut m = Model::new("Test");
        for &(cat_name, items) in cats {
            m.add_category(cat_name).unwrap();
            let cat = m.category_mut(cat_name).unwrap();
            for &item in items {
                cat.add_item(item);
            }
        }
        m
    }

    #[test]
    fn empty_model_has_only_virtual_categories() {
        let m = Model::new("Test");
        let tree = build_cat_tree(&m, &HashSet::new());
        // Virtual categories (_Index, _Dim) should appear
        let names: Vec<&str> = tree.iter().map(|e| e.cat_name()).collect();
        assert!(names.contains(&"_Index"));
        assert!(names.contains(&"_Dim"));
    }

    #[test]
    fn collapsed_category_shows_header_only() {
        let m = make_model_with_categories(&[("Region", &["North", "South"])]);
        let tree = build_cat_tree(&m, &HashSet::new());
        let region_entries: Vec<_> = tree.iter().filter(|e| e.cat_name() == "Region").collect();
        assert_eq!(region_entries.len(), 1); // just the header
        assert!(matches!(
            region_entries[0],
            CatTreeEntry::Category {
                expanded: false,
                item_count: 2,
                ..
            }
        ));
    }

    #[test]
    fn expanded_category_shows_items() {
        let m = make_model_with_categories(&[("Region", &["North", "South"])]);
        let mut expanded = HashSet::new();
        expanded.insert("Region".to_string());
        let tree = build_cat_tree(&m, &expanded);
        let region_entries: Vec<_> = tree.iter().filter(|e| e.cat_name() == "Region").collect();
        // Header + 2 items
        assert_eq!(region_entries.len(), 3);
        assert!(matches!(
            region_entries[0],
            CatTreeEntry::Category { expanded: true, .. }
        ));
        assert!(matches!(region_entries[1], CatTreeEntry::Item { .. }));
        assert!(matches!(region_entries[2], CatTreeEntry::Item { .. }));
    }

    #[test]
    fn mixed_expanded_and_collapsed() {
        let m = make_model_with_categories(&[
            ("Region", &["North", "South"]),
            ("Product", &["Shirts", "Pants", "Hats"]),
        ]);
        let mut expanded = HashSet::new();
        expanded.insert("Product".to_string());
        let tree = build_cat_tree(&m, &expanded);

        let region_items: Vec<_> = tree
            .iter()
            .filter(|e| e.cat_name() == "Region" && matches!(e, CatTreeEntry::Item { .. }))
            .collect();
        let product_items: Vec<_> = tree
            .iter()
            .filter(|e| e.cat_name() == "Product" && matches!(e, CatTreeEntry::Item { .. }))
            .collect();
        assert_eq!(region_items.len(), 0); // collapsed
        assert_eq!(product_items.len(), 3); // expanded
    }

    #[test]
    fn cat_name_works_for_both_variants() {
        let header = CatTreeEntry::Category {
            name: "Region".into(),
            item_count: 2,
            expanded: false,
        };
        let item = CatTreeEntry::Item {
            cat_name: "Region".into(),
            item_name: "North".into(),
        };
        assert_eq!(header.cat_name(), "Region");
        assert_eq!(item.cat_name(), "Region");
    }

    #[test]
    fn expanding_nonexistent_category_is_harmless() {
        let m = Model::new("Test");
        let mut expanded = HashSet::new();
        expanded.insert("DoesNotExist".to_string());
        let tree = build_cat_tree(&m, &expanded);
        // Should just have virtual categories, no crash
        assert!(!tree.is_empty());
    }
}
