use anyhow::Result;

use astmap_core::model::ImpactResult;
use astmap_core::QueryStore;

use super::resolve_symbol_id;

pub fn impact(db: &dyn QueryStore, symbol: &str, depth: i64) -> Result<ImpactResult> {
    let sym_id = resolve_symbol_id(db, symbol)?;
    let entries = db.impact_analysis(sym_id, depth)?;
    Ok(ImpactResult {
        symbol_id: sym_id,
        entries,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::infra::discovery::IgnoreWalkDiscovery;
    use astmap_languages::DefaultLanguageRegistry;
    use std::fs;

    fn setup_db(dir: &std::path::Path) -> Box<dyn QueryStore> {
        let db = crate::infra::sqlite::open(dir).unwrap();
        astmap_core::scanner::scan(
            dir,
            db.as_ref(),
            true,
            &DefaultLanguageRegistry,
            &IgnoreWalkDiscovery,
        )
        .unwrap();
        db
    }

    #[test]
    fn impact_by_name() {
        let dir = tempfile::tempdir().unwrap();
        let src = dir.path().join("src");
        fs::create_dir_all(&src).unwrap();
        fs::write(src.join("lib.rs"), "pub fn hello() {}").unwrap();

        let db = setup_db(dir.path());
        let result = impact(db.as_ref(), "hello", 3).unwrap();
        assert!(result.symbol_id > 0);
    }

    #[test]
    fn impact_by_id() {
        let dir = tempfile::tempdir().unwrap();
        let src = dir.path().join("src");
        fs::create_dir_all(&src).unwrap();
        fs::write(src.join("lib.rs"), "pub fn hello() {}").unwrap();

        let db = setup_db(dir.path());
        let result = impact(db.as_ref(), "hello", 3).unwrap();
        let result2 = impact(db.as_ref(), &result.symbol_id.to_string(), 3).unwrap();
        assert_eq!(result.symbol_id, result2.symbol_id);
    }

    #[test]
    fn impact_not_found() {
        let dir = tempfile::tempdir().unwrap();
        let src = dir.path().join("src");
        fs::create_dir_all(&src).unwrap();
        fs::write(src.join("lib.rs"), "pub fn hello() {}").unwrap();

        let db = setup_db(dir.path());
        assert!(impact(db.as_ref(), "nonexistent", 3).is_err());
    }

    #[test]
    fn impact_with_dependents() {
        let dir = tempfile::tempdir().unwrap();
        let src = dir.path().join("src");
        fs::create_dir_all(&src).unwrap();
        fs::write(
            src.join("lib.rs"),
            "pub fn helper() {}\npub fn caller() { helper(); }",
        )
        .unwrap();

        let db = setup_db(dir.path());
        let result = impact(db.as_ref(), "helper", 3).unwrap();
        // caller depends on helper, so impact should include it
        let names: Vec<_> = result
            .entries
            .iter()
            .map(|e| e.symbol_name.as_str())
            .collect();
        assert!(
            names.contains(&"caller"),
            "expected caller in impact: {:?}",
            names
        );
    }
}
