// Agent definition loader - loads agent configurations from JSON files
// Phase 3: Support for agent definition files

use std::fs;
use std::path::Path;

use serde_json;
use thiserror::Error;

use crate::config::AgentDefinition;

#[derive(Debug, Error)]
pub enum LoaderError {
    #[error("Failed to read file: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Failed to parse JSON: {0}")]
    JsonError(#[from] serde_json::Error),

    #[error("Agent not found: {0}")]
    AgentNotFound(String),

    #[error("Invalid agent definition: {0}")]
    InvalidDefinition(String),
}

pub type Result<T> = std::result::Result<T, LoaderError>;

/// Load agent definitions from JSON files
pub struct AgentDefinitionLoader;

impl AgentDefinitionLoader {
    /// Load a single agent definition from a JSON file
    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<AgentDefinition> {
        let content = fs::read_to_string(path)?;
        let definition = serde_json::from_str(&content)?;
        Ok(definition)
    }

    /// Load all agent definitions from a directory
    pub fn load_from_directory<P: AsRef<Path>>(directory: P) -> Result<Vec<AgentDefinition>> {
        let dir = directory.as_ref();

        if !dir.exists() {
            return Err(LoaderError::AgentNotFound(format!(
                "Directory not found: {}",
                dir.display()
            )));
        }

        let mut definitions = Vec::new();

        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();

            // Only load .json files
            if path.extension().is_some_and(|ext| ext == "json") {
                match Self::load_from_file(&path) {
                    Ok(definition) => definitions.push(definition),
                    Err(e) => {
                        // Log warning but continue loading other files
                        eprintln!("Warning: Failed to load {}: {}", path.display(), e);
                    }
                }
            }
        }

        if definitions.is_empty() {
            return Err(LoaderError::AgentNotFound(format!(
                "No agent definitions found in {}",
                dir.display()
            )));
        }

        Ok(definitions)
    }

    /// Load a specific agent by role from a directory
    pub fn load_by_role<P: AsRef<Path>>(directory: P, role: &str) -> Result<AgentDefinition> {
        let definitions = Self::load_from_directory(directory)?;

        definitions
            .into_iter()
            .find(|def| def.role == role)
            .ok_or_else(|| LoaderError::AgentNotFound(role.to_string()))
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;

    use serde_json::json;
    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_load_from_file() -> Result<()> {
        let temp_dir = TempDir::new().map_err(|e| LoaderError::IoError(e))?;
        let file_path = temp_dir.path().join("test.json");

        let definition = json!({
            "role": "developer",
            "description": "Test Developer",
            "llm_provider": "claude",
            "llm_model": "claude-sonnet-4-5",
            "capabilities": ["code_generation"],
            "parallelizable": true,
            "priority": 50
        });

        let mut file = fs::File::create(&file_path)?;
        file.write_all(serde_json::to_string(&definition)?.as_bytes())?;

        let loaded = AgentDefinitionLoader::load_from_file(&file_path)?;
        assert_eq!(loaded.role, "developer");
        assert_eq!(loaded.description, "Test Developer");

        Ok(())
    }

    #[test]
    fn test_load_from_directory() -> Result<()> {
        let temp_dir = TempDir::new().map_err(|e| LoaderError::IoError(e))?;

        // Create multiple agent files
        for (role, desc) in &[("developer", "Developer"), ("reviewer", "Reviewer")] {
            let definition = json!({
                "role": role,
                "description": desc,
                "llm_provider": "claude",
                "llm_model": "claude-sonnet-4-5",
                "capabilities": ["testing"],
                "parallelizable": false,
                "priority": 50
            });

            let file_path = temp_dir.path().join(format!("{}.json", role));
            let mut file = fs::File::create(&file_path)?;
            file.write_all(serde_json::to_string(&definition)?.as_bytes())?;
        }

        let definitions = AgentDefinitionLoader::load_from_directory(temp_dir.path())?;
        assert_eq!(definitions.len(), 2);

        Ok(())
    }

    #[test]
    fn test_load_by_role() -> Result<()> {
        let temp_dir = TempDir::new().map_err(|e| LoaderError::IoError(e))?;

        let definition = json!({
            "role": "developer",
            "description": "Developer",
            "llm_provider": "claude",
            "llm_model": "claude-sonnet-4-5",
            "capabilities": ["coding"],
            "parallelizable": true,
            "priority": 50
        });

        let file_path = temp_dir.path().join("developer.json");
        let mut file = fs::File::create(&file_path)?;
        file.write_all(serde_json::to_string(&definition)?.as_bytes())?;

        let loaded = AgentDefinitionLoader::load_by_role(temp_dir.path(), "developer")?;
        assert_eq!(loaded.role, "developer");

        Ok(())
    }
}
