---
to: "{{ name | snake_case }}_client.rs"
vars:
  name: "IntelligentAssistant"
  description: "AI-powered assistant for {{ name }}"
  provider: "ollama"  # openai, anthropic, ollama, mock
  model: "qwen3-coder:30b"
  enable_streaming: true
  enable_tools: true
  custom_tools: []
rdf:
  - "{{ name | snake_case }}_client.ttl"
---

// AI Client Wrapper: {{ 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, UsageStats};
use ggen_ai::providers::{OpenAIClient, AnthropicClient, OllamaClient, MockClient};
use ggen_ai::generators::{TemplateGenerator, SparqlGenerator, OntologyGenerator, RefactorAssistant};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;

/// Configuration for {{ name }} AI client
#[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,
    /// Custom tools to register
    pub custom_tools: Vec<String>,
}

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

/// Trait for custom tools that can be registered with the AI client
#[async_trait]
pub trait Tool: Send + Sync {
    /// Get the tool name
    fn name(&self) -> &str;
    /// Get the tool description
    fn description(&self) -> &str;
    /// Execute the tool with parameters
    async fn execute(&self, params: Value) -> Result<Value>;
}

impl {{ name }}Client {
    /// Create a new {{ name }} client with configuration
    pub fn new(config: {{ name }}Config) -> Result<Self> {
        let client = Self::create_provider_client(&config)?;
        let mut tools = HashMap::new();

        // Register custom tools
        for tool_name in &config.custom_tools {
            if let Some(tool) = Self::create_custom_tool(tool_name) {
                tools.insert(tool_name.clone(), tool);
            }
        }

        Ok(Self {
            client,
            config,
            template_generator: None,
            sparql_generator: None,
            ontology_generator: None,
            refactor_assistant: None,
            tools,
        })
    }

    /// Initialize generators based on configuration
    pub fn initialize_generators(mut self) -> 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()
        };

        // Initialize generators with the same provider
        let client1 = Self::create_provider_client(&self.config).unwrap();
        let client2 = Self::create_provider_client(&self.config).unwrap();
        let client3 = Self::create_provider_client(&self.config).unwrap();
        let client4 = Self::create_provider_client(&self.config).unwrap();

        self.template_generator = Some(TemplateGenerator::with_config(client1, llm_config.clone()));
        self.sparql_generator = Some(SparqlGenerator::with_config(client2, llm_config.clone()));
        self.ontology_generator = Some(OntologyGenerator::with_config(client3, llm_config.clone()));
        self.refactor_assistant = Some(RefactorAssistant::with_config(client4, llm_config));

        self
    }

    /// 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)),
        }
    }

    /// Create a custom tool instance
    fn create_custom_tool(tool_name: &str) -> Option<Box<dyn Tool>> {
        // This would be extended to create actual tool implementations
        // For now, return None for unknown tools
        None
    }

    /// Complete a prompt using the AI provider
    pub async fn complete(&self, prompt: &str) -> Result<LlmResponse> {
        self.client.complete(prompt, None).await
    }

    /// Complete a prompt with streaming
    pub async fn complete_stream(&self, prompt: &str) -> Result<impl StreamExt<Item = LlmChunk>> {
        self.client.complete_stream(prompt, None).await
    }

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

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

    /// 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 initialized. Call initialize_generators() first."))
        }
    }

    /// 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 initialized. Call initialize_generators() first."))
        }
    }

    /// Execute a custom tool
    pub async fn execute_tool(&self, tool_name: &str, params: Value) -> Result<Value> {
        if let Some(tool) = self.tools.get(tool_name) {
            tool.execute(params).await
        } else {
            Err(anyhow::anyhow!("Tool '{}' not found", tool_name))
        }
    }

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

        if self.template_generator.is_some() {
            tools.push("template_generation".to_string());
        }
        if self.sparql_generator.is_some() {
            tools.push("sparql_generation".to_string());
        }
        if self.ontology_generator.is_some() {
            tools.push("ontology_generation".to_string());
        }
        if self.refactor_assistant.is_some() {
            tools.push("code_refactoring".to_string());
        }

        tools.extend(self.tools.keys().cloned());
        tools
    }
}

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 }},
            custom_tools: vec![],
        }
    }
}

/// Example custom tool implementation
pub struct ExampleTool;

#[async_trait]
impl Tool for ExampleTool {
    fn name(&self) -> &str {
        "example_tool"
    }

    fn description(&self) -> &str {
        "An example custom tool for demonstration"
    }

    async fn execute(&self, params: Value) -> Result<Value> {
        // Example tool implementation
        Ok(Value::String("Example tool executed".to_string()))
    }
}

#[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);
    }

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

        let client = {{ name }}Client::new(config);
        assert!(client.is_ok());
    }

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

        let client = {{ name }}Client::new(config).unwrap().initialize_generators();
        let tools = client.get_available_tools();

        assert!(!tools.is_empty());
        assert!(tools.contains(&"template_generation".to_string()));
    }
}
