//! Rust code generator for protobuf files

use anyhow::{Context, Result};
use std::path::PathBuf;

/// Rust protobuf code generator
pub struct RustGenerator {
    project_root: PathBuf,
}

impl RustGenerator {
    /// Create a new Rust generator
    pub fn new(project_root: PathBuf) -> Self {
        Self { project_root }
    }

    /// Generate Rust code from proto files
    pub fn generate(&self) -> Result<()> {
        let proto_dir = self.project_root.join("protos");
        let output_dir = self.project_root.join("src").join("protos");

        // Ensure output directory exists
        std::fs::create_dir_all(&output_dir)
            .context("Failed to create Rust output directory")?;

        // Find all .proto files
        let proto_files = self.find_proto_files(&proto_dir)?;

        if proto_files.is_empty() {
            println!("⚠️  No .proto files found in {}", proto_dir.display());
            return Ok(());
        }

        println!("📁 Found {} proto file(s)", proto_files.len());

        // Configure prost-build
        let mut config = prost_build::Config::new();
        config.out_dir(&output_dir);

        // Add type attributes for serde support
        config.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]");

        // Compile proto files
        config
            .compile_protos(&proto_files, &[&proto_dir])
            .context("Failed to compile proto files")?;

        // Generate mod.rs file
        self.generate_mod_file(&output_dir, &proto_files)?;

        println!("📝 Generated Rust code in {}", output_dir.display());

        Ok(())
    }

    /// Find all .proto files in the given directory
    fn find_proto_files(&self, dir: &PathBuf) -> Result<Vec<PathBuf>> {
        let mut proto_files = Vec::new();

        if !dir.exists() {
            return Ok(proto_files);
        }

        for entry in std::fs::read_dir(dir).context("Failed to read proto directory")? {
            let entry = entry?;
            let path = entry.path();

            if path.is_file() && path.extension().is_some_and(|ext| ext == "proto") {
                proto_files.push(path);
            }
        }

        Ok(proto_files)
    }

    /// Generate mod.rs file for the generated code
    fn generate_mod_file(&self, output_dir: &PathBuf, proto_files: &[PathBuf]) -> Result<()> {
        let mut mod_content = String::from("//! Generated protobuf modules\n\n");

        for proto_file in proto_files {
            if let Some(stem) = proto_file.file_stem() {
                let module_name = stem.to_string_lossy().replace('-', "_");
                mod_content.push_str(&format!("pub mod {module_name};\n"));
            }
        }

        let mod_file = output_dir.join("mod.rs");
        std::fs::write(&mod_file, mod_content)
            .context("Failed to write mod.rs file")?;

        Ok(())
    }
}
