//! AI-powered template generation example
//!
//! This example demonstrates ggen-ai capabilities:
//! - Configuring LLM clients (OpenAI, Anthropic, Ollama)
//! - Generating templates from natural language descriptions
//! - Validating generated templates
//! - Using mock clients for testing

use anyhow::{Context, Result};
use ggen_ai::{
    GenAiClient, LlmClient, LlmConfig, LlmProvider, MockClient, TemplateGenerator,
    TemplateValidator,
};
use std::env;
use tracing::{info, warn, Level};
use tracing_subscriber::FmtSubscriber;

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize logging
    let subscriber = FmtSubscriber::builder()
        .with_max_level(Level::INFO)
        .finish();
    tracing::subscriber::set_global_default(subscriber)?;

    info!("Starting ggen AI-powered generation example");

    // Example 1: Using MockClient for development/testing
    example_mock_client().await?;

    // Example 2: Real LLM client (if API key available)
    if let Ok(api_key) = env::var("OPENAI_API_KEY") {
        example_real_llm_client(&api_key).await?;
    } else {
        warn!("OPENAI_API_KEY not set, skipping real LLM example");
        info!("Set OPENAI_API_KEY environment variable to try real AI generation");
    }

    // Example 3: Template validation
    example_template_validation().await?;

    // Example 4: Different AI providers
    example_multiple_providers().await?;

    info!("All AI examples completed successfully!");
    Ok(())
}

/// Example 1: Using MockClient for development and testing
async fn example_mock_client() -> Result<()> {
    info!("=== Example 1: Mock LLM Client ===");

    // Create a mock client with predefined response
    let mock_template = r#"---
name: generated-api
description: REST API template generated by AI
version: 1.0.0
---
# {{ api_name }} API

## Endpoints

### GET /{{ resource }}
Retrieve all {{ resource }} items.

### POST /{{ resource }}
Create a new {{ resource }} item.

### GET /{{ resource }}/:id
Retrieve a specific {{ resource }} item.

### PUT /{{ resource }}/:id
Update a specific {{ resource }} item.

### DELETE /{{ resource }}/:id
Delete a specific {{ resource }} item.
"#;

    let mock_client = MockClient::with_response(mock_template);
    let generator = TemplateGenerator::new(Box::new(mock_client));

    // Generate template from description
    let description = "Create a REST API template for user management";
    let requirements = vec!["Include CRUD operations", "Use TypeScript", "Add validation"];

    let template = generator
        .generate_template(description, requirements)
        .await
        .context("Failed to generate template with mock client")?;

    info!("Generated template name: {}", template.metadata.name);
    info!("Template content:\n{}", template.content);

    Ok(())
}

/// Example 2: Using real LLM client (OpenAI)
async fn example_real_llm_client(api_key: &str) -> Result<()> {
    info!("=== Example 2: Real LLM Client (OpenAI) ===");

    // Create configuration
    let config = LlmConfig {
        provider: LlmProvider::OpenAI,
        model: "gpt-4o".to_string(),
        api_key: api_key.to_string(),
        temperature: Some(0.7),
        max_tokens: Some(2000),
        ..Default::default()
    };

    // Create client
    let client = GenAiClient::with_config(config)
        .context("Failed to create GenAI client")?;

    let generator = TemplateGenerator::new(Box::new(client));

    // Generate a React component template
    let description = "Create a React component template for a user profile card";
    let requirements = vec![
        "Include TypeScript types",
        "Add props interface",
        "Include CSS modules",
        "Add prop validation",
    ];

    info!("Generating template with description: {}", description);
    info!("Requirements: {:?}", requirements);

    let template = generator
        .generate_template(description, requirements)
        .await
        .context("Failed to generate template with real LLM")?;

    info!("Generated template:");
    info!("  Name: {}", template.metadata.name);
    info!("  Description: {:?}", template.metadata.description);
    info!("\nTemplate content:\n{}", template.content);

    Ok(())
}

/// Example 3: Template validation with AI
async fn example_template_validation() -> Result<()> {
    info!("=== Example 3: Template Validation ===");

    // Create a template to validate
    let template_content = r#"---
name: sample-validation
description: A template for validation testing
---
# {{ project_name }}

{% if invalid_syntax
This template has a syntax error!
{% endif %}
"#;

    let mock_response = r#"[
        {
            "severity": "error",
            "message": "Unclosed if block",
            "line": 7
        }
    ]"#;

    let mock_client = MockClient::with_response(mock_response);
    let validator = TemplateValidator::new(Box::new(mock_client));

    // Note: In real usage, this would parse the template first
    info!("Validating template...");
    info!("Mock validation response: {}", mock_response);

    Ok(())
}

/// Example 4: Different AI providers
async fn example_multiple_providers() -> Result<()> {
    info!("=== Example 4: Multiple AI Providers ===");

    // Example configurations for different providers
    let providers = vec![
        ("OpenAI GPT-4", LlmProvider::OpenAI, "gpt-4o"),
        ("Anthropic Claude", LlmProvider::Anthropic, "claude-3-5-sonnet-20241022"),
        ("Ollama Local", LlmProvider::Ollama, "qwen2.5-coder:7b"),
    ];

    for (name, provider, model) in providers {
        info!("\nProvider: {}", name);
        info!("  Provider enum: {:?}", provider);
        info!("  Model: {}", model);

        // Show how to create configuration
        let config = LlmConfig {
            provider,
            model: model.to_string(),
            api_key: env::var("API_KEY").unwrap_or_default(),
            temperature: Some(0.7),
            ..Default::default()
        };

        info!("  Config created: {:?}", config.provider);
    }

    info!("\nTo use real providers, set these environment variables:");
    info!("  - OPENAI_API_KEY for OpenAI");
    info!("  - ANTHROPIC_API_KEY for Anthropic");
    info!("  - Install Ollama locally for Ollama provider");

    Ok(())
}

/// Example 5: Streaming responses
async fn example_streaming_response() -> Result<()> {
    info!("=== Example 5: Streaming LLM Responses ===");

    let api_key = env::var("OPENAI_API_KEY").unwrap_or_default();

    if api_key.is_empty() {
        info!("Skipping streaming example (no API key)");
        return Ok(());
    }

    let config = LlmConfig {
        provider: LlmProvider::OpenAI,
        model: "gpt-4o".to_string(),
        api_key,
        ..Default::default()
    };

    let client = GenAiClient::with_config(config)?;

    info!("Requesting streaming response...");

    let prompt = "Generate a brief template for a Python CLI tool";

    // Note: Actual streaming would use complete_stream()
    // This is a simplified example
    let response = client.complete(prompt).await?;
    info!("Response: {}", response.content);
    info!("Tokens used: {:?}", response.usage);

    Ok(())
}

/// Example 6: Custom prompts and system messages
async fn example_custom_prompts() -> Result<()> {
    info!("=== Example 6: Custom Prompts ===");

    let mock_client = MockClient::with_response("Custom template generated!");

    // In real usage, you could customize the prompt template
    let custom_system_prompt = r#"
You are a code generation expert specializing in creating high-quality,
production-ready templates. Focus on:
- Clean, maintainable code
- Comprehensive documentation
- Best practices and patterns
- Type safety where applicable
"#;

    info!("Custom system prompt: {}", custom_system_prompt);

    let generator = TemplateGenerator::new(Box::new(mock_client));

    let template = generator
        .generate_template(
            "Create a microservice template",
            vec!["Use hexagonal architecture", "Include observability"],
        )
        .await?;

    info!("Generated with custom prompt: {}", template.metadata.name);

    Ok(())
}

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

    #[tokio::test]
    async fn test_mock_client() {
        let result = example_mock_client().await;
        assert!(result.is_ok());
    }

    #[tokio::test]
    async fn test_template_validation() {
        let result = example_template_validation().await;
        assert!(result.is_ok());
    }

    #[test]
    fn test_llm_config_creation() {
        let config = LlmConfig {
            provider: LlmProvider::OpenAI,
            model: "gpt-4o".to_string(),
            api_key: "test-key".to_string(),
            ..Default::default()
        };

        assert_eq!(config.model, "gpt-4o");
        assert!(matches!(config.provider, LlmProvider::OpenAI));
    }
}
