use std::collections::HashMap;
use std::collections::hash_map::Entry;

use miette::Result;

use super::extract::{self as extract, GoPackage};
use super::project::Project;
use super::resolve::{ImportKind, Resolver};

/// Cache for parsed Go packages.
/// Avoids re-parsing the same package multiple times during compilation.
pub struct GoCache {
    /// Cached packages by import path
    packages: HashMap<String, GoPackage>,
    /// Resolver for finding package sources
    resolver: Resolver,
}

impl GoCache {
    /// Create a new cache with a resolver
    pub fn new() -> Result<Self> {
        Ok(Self {
            packages: HashMap::new(),
            resolver: Resolver::new()?,
        })
    }

    /// Get a package, parsing it if not cached
    ///
    /// - Stdlib packages work without a project
    /// - External modules require a project for version resolution
    pub fn get_or_parse(
        &mut self,
        import_path: &str,
        project: Option<&Project>,
    ) -> Result<&GoPackage> {
        // Use entry API to avoid double lookup
        if let Entry::Vacant(entry) = self.packages.entry(import_path.to_string()) {
            let kind = self.resolver.resolve(import_path, project)?;
            let source_dir = match &kind {
                ImportKind::LocalSoppo { source_dir } => source_dir,
                ImportKind::GoStdlib { source_dir, .. } => source_dir,
                ImportKind::ExternalGo { source_dir, .. } => source_dir,
            };

            let pkg = extract::extract(source_dir)?;
            entry.insert(pkg);
        }

        Ok(self.packages.get(import_path).unwrap())
    }

    /// Check if a package is cached
    pub fn is_cached(&self, import_path: &str) -> bool {
        self.packages.contains_key(import_path)
    }

    /// Clear the cache
    pub fn clear(&mut self) {
        self.packages.clear();
    }

    /// Get the resolver for direct access
    pub fn resolver(&self) -> &Resolver {
        &self.resolver
    }
}

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

    #[test]
    fn test_cache_creation() {
        let cache = GoCache::new();
        assert!(cache.is_ok());
    }

    #[test]
    fn test_parse_go_file() {
        use std::fs::{self, File};
        use std::io::Write;

        let dir = std::env::temp_dir().join("soppo-cache-test");
        let _ = fs::remove_dir_all(&dir);
        fs::create_dir_all(&dir).unwrap();

        let file_path = dir.join("test.go");
        let mut file = File::create(&file_path).unwrap();
        writeln!(
            file,
            r#"
package test

func Hello() string {{
    return "hello"
}}
"#
        )
        .unwrap();

        let pkg = extract::extract(&file_path).unwrap();
        assert!(pkg.functions.contains_key("Hello"));

        fs::remove_dir_all(&dir).unwrap();
    }
}
