---
to: "{{ name | snake_case }}_generators.rs"
vars:
  name: "CodeAssistant"
  description: "AI-powered code generation assistant"
  provider: "ollama"
  model: "qwen3-coder:30b"
  generators: ["template", "sparql", "ontology", "refactor"]
  enable_streaming: true
  enable_caching: true
  custom_prompts: []
rdf:
  - "{{ name | snake_case }}_generators.ttl"
---

// AI-Powered Generators: {{ description }}
// Generated by ggen-ai using {{ provider }} provider

use anyhow::Result;
use async_trait::async_trait;
use futures::StreamExt;
use ggen_ai::client::{LlmClient, LlmConfig, LlmResponse, LlmChunk};
use ggen_ai::generators::{TemplateGenerator, SparqlGenerator, OntologyGenerator, RefactorAssistant};
use ggen_ai::providers::{OpenAIClient, AnthropicClient, OllamaClient, MockClient};
use ggen_core::{Graph, Template};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;

/// Configuration for {{ name }} AI generators
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {{ name }}Config {
    /// AI provider to use (openai, anthropic, ollama, mock)
    pub provider: String,
    /// Model to use for completions
    pub model: String,
    /// API key for the provider
    pub api_key: Option<String>,
    /// Maximum tokens per request
    pub max_tokens: Option<u32>,
    /// Temperature for response randomness (0.0 - 2.0)
    pub temperature: Option<f32>,
    /// Enable streaming responses
    pub streaming: bool,
    /// Enable caching of results
    pub caching: bool,
    /// Which generators to enable
    pub enabled_generators: Vec<String>,
    /// Custom prompts to use
    pub custom_prompts: HashMap<String, String>,
}

/// {{ name }} AI generators with all capabilities
#[derive(Debug)]
pub struct {{ name }}Generators {
    config: {{ name }}Config,
    client: Box<dyn LlmClient>,
    template_generator: Option<TemplateGenerator>,
    sparql_generator: Option<SparqlGenerator>,
    ontology_generator: Option<OntologyGenerator>,
    refactor_assistant: Option<RefactorAssistant>,
    cache: Option<Arc<HashMap<String, String>>>,
}

/// Individual generator trait for extensibility
#[async_trait]
pub trait AiGenerator: Send + Sync {
    /// Get the generator name
    fn name(&self) -> &str;
    /// Generate content based on description
    async fn generate(&self, description: &str, context: Option<Value>) -> Result<String>;
    /// Validate the generated content
    fn validate(&self, content: &str) -> Result<()>;
}

/// Template-specific generator
pub struct TemplateAiGenerator {
    generator: TemplateGenerator,
}

#[async_trait]
impl AiGenerator for TemplateAiGenerator {
    fn name(&self) -> &str {
        "template"
    }

    async fn generate(&self, description: &str, context: Option<Value>) -> Result<String> {
        let examples = if let Some(ctx) = context {
            if let Some(examples) = ctx.get("examples") {
                if let Some(arr) = examples.as_array() {
                    arr.iter().filter_map(|v| v.as_str()).collect()
                } else {
                    vec![]
                }
            } else {
                vec![]
            }
        } else {
            vec![]
        };

        let template = self.generator.generate_template(description, examples).await?;
        Ok(template.to_string())
    }

    fn validate(&self, content: &str) -> Result<()> {
        // Basic template validation - check for balanced braces
        let open_braces = content.chars().filter(|&c| c == '{').count();
        let close_braces = content.chars().filter(|&c| c == '}').count();

        if open_braces != close_braces {
            return Err(anyhow::anyhow!("Unbalanced braces in template"));
        }

        Ok(())
    }
}

/// SPARQL-specific generator
pub struct SparqlAiGenerator {
    generator: SparqlGenerator,
}

#[async_trait]
impl AiGenerator for SparqlAiGenerator {
    fn name(&self) -> &str {
        "sparql"
    }

    async fn generate(&self, description: &str, context: Option<Value>) -> Result<String> {
        let graph = if let Some(ctx) = context {
            if let Some(graph_data) = ctx.get("graph") {
                // Parse graph from context if provided
                // For now, assume empty graph
                Graph::new()
            } else {
                Graph::new()
            }
        } else {
            Graph::new()
        };

        self.generator.generate_sparql(description, &graph).await
    }

    fn validate(&self, content: &str) -> Result<()> {
        // Basic SPARQL validation - check for SELECT/PREFIX keywords
        if !content.to_uppercase().contains("SELECT") && !content.to_uppercase().contains("CONSTRUCT") {
            return Err(anyhow::anyhow!("SPARQL query must contain SELECT or CONSTRUCT"));
        }

        Ok(())
    }
}

/// Ontology-specific generator
pub struct OntologyAiGenerator {
    generator: OntologyGenerator,
}

#[async_trait]
impl AiGenerator for OntologyAiGenerator {
    fn name(&self) -> &str {
        "ontology"
    }

    async fn generate(&self, description: &str, context: Option<Value>) -> Result<String> {
        let base_iri = if let Some(ctx) = context {
            ctx.get("base_iri").and_then(|v| v.as_str()).unwrap_or("http://example.org/")
        } else {
            "http://example.org/"
        };

        self.generator.generate_ontology(description, base_iri).await
    }

    fn validate(&self, content: &str) -> Result<()> {
        // Basic ontology validation - check for RDF/Turtle syntax
        if !content.contains("@prefix") && !content.contains("PREFIX") {
            return Err(anyhow::anyhow!("Ontology should contain prefix declarations"));
        }

        Ok(())
    }
}

/// Refactoring-specific generator
pub struct RefactorAiGenerator {
    assistant: RefactorAssistant,
}

#[async_trait]
impl AiGenerator for RefactorAiGenerator {
    fn name(&self) -> &str {
        "refactor"
    }

    async fn generate(&self, description: &str, context: Option<Value>) -> Result<String> {
        let code = if let Some(ctx) = context {
            ctx.get("code").and_then(|v| v.as_str()).unwrap_or("")
        } else {
            ""
        };

        let refactoring_type = if let Some(ctx) = context {
            ctx.get("refactoring_type").and_then(|v| v.as_str()).unwrap_or("improve")
        } else {
            "improve"
        };

        self.assistant.refactor_code(code, refactoring_type).await
    }

    fn validate(&self, content: &str) -> Result<()> {
        // Basic refactoring validation - check that code compiles (basic syntax check)
        if content.trim().is_empty() {
            return Err(anyhow::anyhow!("Refactored code cannot be empty"));
        }

        // Check for balanced brackets
        let open_brackets = content.chars().filter(|&c| c == '{').count();
        let close_brackets = content.chars().filter(|&c| c == '}').count();

        if open_brackets != close_brackets {
            return Err(anyhow::anyhow!("Unbalanced brackets in refactored code"));
        }

        Ok(())
    }
}

impl {{ name }}Generators {
    /// Create new {{ name }} generators with configuration
    pub fn new(config: {{ name }}Config) -> Result<Self> {
        let client = Self::create_provider_client(&config)?;
        let cache = if config.caching {
            Some(Arc::new(HashMap::new()))
        } else {
            None
        };

        let mut generators = Self {
            config,
            client,
            template_generator: None,
            sparql_generator: None,
            ontology_generator: None,
            refactor_assistant: None,
            cache,
        };

        generators.initialize_generators();
        Ok(generators)
    }

    /// Create provider client based on configuration
    fn create_provider_client(config: &{{ name }}Config) -> Result<Box<dyn LlmClient>> {
        match config.provider.as_str() {
            "openai" => {
                let api_key = config.api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("API key required for OpenAI"))?;
                Ok(Box::new(OpenAIClient::new(api_key.clone())))
            }
            "anthropic" => {
                let api_key = config.api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("API key required for Anthropic"))?;
                Ok(Box::new(AnthropicClient::new(api_key.clone())))
            }
            "ollama" => {
                Ok(Box::new(OllamaClient::new()))
            }
            "mock" => {
                Ok(Box::new(MockClient::new()))
            }
            _ => Err(anyhow::anyhow!("Unsupported provider: {}", config.provider)),
        }
    }

    /// Initialize generators based on enabled generators list
    fn initialize_generators(&mut self) {
        let llm_config = LlmConfig {
            model: self.config.model.clone(),
            max_tokens: self.config.max_tokens,
            temperature: self.config.temperature,
            streaming: self.config.streaming,
            ..Default::default()
        };

        for generator_name in &self.config.enabled_generators {
            match generator_name.as_str() {
                "template" => {
                    let client = Self::create_provider_client(&self.config).unwrap();
                    self.template_generator = Some(TemplateGenerator::with_config(client, llm_config.clone()));
                }
                "sparql" => {
                    let client = Self::create_provider_client(&self.config).unwrap();
                    self.sparql_generator = Some(SparqlGenerator::with_config(client, llm_config.clone()));
                }
                "ontology" => {
                    let client = Self::create_provider_client(&self.config).unwrap();
                    self.ontology_generator = Some(OntologyGenerator::with_config(client, llm_config.clone()));
                }
                "refactor" => {
                    let client = Self::create_provider_client(&self.config).unwrap();
                    self.refactor_assistant = Some(RefactorAssistant::with_config(client, llm_config.clone()));
                }
                _ => {
                    eprintln!("Warning: Unknown generator: {}", generator_name);
                }
            }
        }
    }

    /// Generate template using AI
    pub async fn generate_template(&self, description: &str, examples: Vec<&str>) -> Result<Template> {
        if let Some(generator) = &self.template_generator {
            generator.generate_template(description, examples).await
        } else {
            Err(anyhow::anyhow!("Template generator not enabled or initialized"))
        }
    }

    /// Generate SPARQL query using AI
    pub async fn generate_sparql(&self, description: &str, graph: &Graph) -> Result<String> {
        if let Some(generator) = &self.sparql_generator {
            generator.generate_sparql(description, graph).await
        } else {
            Err(anyhow::anyhow!("SPARQL generator not enabled or initialized"))
        }
    }

    /// Generate ontology using AI
    pub async fn generate_ontology(&self, description: &str, base_iri: &str) -> Result<String> {
        if let Some(generator) = &self.ontology_generator {
            generator.generate_ontology(description, base_iri).await
        } else {
            Err(anyhow::anyhow!("Ontology generator not enabled or initialized"))
        }
    }

    /// Refactor code using AI
    pub async fn refactor_code(&self, code: &str, refactoring_type: &str) -> Result<String> {
        if let Some(assistant) = &self.refactor_assistant {
            assistant.refactor_code(code, refactoring_type).await
        } else {
            Err(anyhow::anyhow!("Refactor assistant not enabled or initialized"))
        }
    }

    /// Get available generators
    pub fn get_available_generators(&self) -> Vec<String> {
        let mut generators = Vec::new();

        if self.template_generator.is_some() {
            generators.push("template".to_string());
        }
        if self.sparql_generator.is_some() {
            generators.push("sparql".to_string());
        }
        if self.ontology_generator.is_some() {
            generators.push("ontology".to_string());
        }
        if self.refactor_assistant.is_some() {
            generators.push("refactor".to_string());
        }

        generators
    }

    /// Check if caching is enabled
    pub fn is_caching_enabled(&self) -> bool {
        self.cache.is_some()
    }

    /// Get cached result if available
    pub fn get_cached(&self, key: &str) -> Option<&String> {
        self.cache.as_ref()?.get(key)
    }

    /// Cache a result
    pub fn cache_result(&self, key: String, value: String) {
        if let Some(cache) = &self.cache {
            // In a real implementation, this would be thread-safe
            // For now, we'll skip caching to avoid complexity
        }
    }
}

impl Default for {{ name }}Config {
    fn default() -> Self {
        Self {
            provider: "{{ provider }}".to_string(),
            model: "qwen3-coder:30b".to_string(),
            api_key: std::env::var("AI_API_KEY").ok(),
            max_tokens: Some(2000),
            temperature: Some(0.7),
            streaming: {{ enable_streaming | lower }},
            caching: {{ enable_caching | lower }},
            enabled_generators: vec!["template".to_string(), "sparql".to_string(), "ontology".to_string(), "refactor".to_string()],
            custom_prompts: HashMap::new(),
        }
    }
}

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

    #[test]
    fn test_config_default() {
        let config = {{ name }}Config::default();
        assert_eq!(config.provider, "{{ provider }}");
        assert_eq!(config.model, "{{ model }}");
        assert!(config.streaming);
        assert!(config.caching);
        assert_eq!(config.enabled_generators.len(), 4);
    }

    #[test]
    fn test_generators_creation() {
        let config = {{ name }}Config {
            provider: "mock".to_string(),
            ..Default::default()
        };

        let generators = {{ name }}Generators::new(config);
        assert!(generators.is_ok());
    }

    #[test]
    fn test_available_generators() {
        let config = {{ name }}Config {
            provider: "mock".to_string(),
            enabled_generators: vec!["template".to_string(), "sparql".to_string()],
            ..Default::default()
        };

        let generators = {{ name }}Generators::new(config).unwrap();
        let available = generators.get_available_generators();

        assert_eq!(available.len(), 2);
        assert!(available.contains(&"template".to_string()));
        assert!(available.contains(&"sparql".to_string()));
    }

    #[test]
    fn test_caching() {
        let config = {{ name }}Config {
            provider: "mock".to_string(),
            caching: true,
            ..Default::default()
        };

        let generators = {{ name }}Generators::new(config).unwrap();
        assert!(generators.is_caching_enabled());
    }
}
