use anyhow::{bail, Result};

use astmap_core::model::InspectResult;
use astmap_core::QueryStore;

use super::resolve_symbol;

pub fn inspect(db: &dyn QueryStore, symbol: &str) -> Result<InspectResult> {
    let sym = resolve_symbol(db, symbol)?;
    match sym {
        Some(s) => {
            let children = db.get_children(s.id)?;
            Ok(InspectResult {
                symbol: s,
                children,
            })
        }
        None => bail!("symbol not found: '{}'", symbol),
    }
}

#[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 inspect_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 = inspect(db.as_ref(), "hello").unwrap();
        assert_eq!(result.symbol.name, "hello");
    }

    #[test]
    fn inspect_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 sym = resolve_symbol(db.as_ref(), "hello").unwrap().unwrap();
        let result = inspect(db.as_ref(), &sym.id.to_string()).unwrap();
        assert_eq!(result.symbol.name, "hello");
    }

    #[test]
    fn inspect_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());
        let result = inspect(db.as_ref(), "nonexistent");
        assert!(result.is_err());
    }

    #[test]
    fn inspect_with_children() {
        let dir = tempfile::tempdir().unwrap();
        let src = dir.path().join("src");
        fs::create_dir_all(&src).unwrap();
        fs::write(
            src.join("lib.rs"),
            "pub struct Foo {}\nimpl Foo {\n    pub fn bar(&self) {}\n}",
        )
        .unwrap();

        let db = setup_db(dir.path());
        let result = inspect(db.as_ref(), "Foo").unwrap();
        assert_eq!(result.symbol.name, "Foo");
        // impl block or methods may appear as children
    }
}
