//! AI-powered FormBackend implementation
//!
//! Provides intelligent form field assistance using LLM + RAG.

use crate::llm::{GenerationOptions, LlmProvider, Message};
use crate::storage::SurrealDbClient;
use async_trait::async_trait;
use serde_json::json;
use std::collections::HashMap;
use std::sync::Arc;
use typedialog_core::ai::rag::RagSystem;
use typedialog_core::backends::{FormBackend, RenderContext};
use typedialog_core::error::Result;
use typedialog_core::form_parser::{DisplayItem, FieldDefinition, FieldType};

/// Interaction mode for AI backend
#[derive(Debug, Clone, Copy)]
pub enum AiBackendMode {
    /// Interactive: LLM suggests, user can override
    Interactive,
    /// AutoComplete: LLM generates all values
    AutoComplete,
    /// ValidateOnly: User provides, LLM validates
    ValidateOnly,
}

/// AI-powered FormBackend
pub struct AiBackend {
    llm: Arc<dyn LlmProvider>,
    #[allow(dead_code)]
    rag: Option<RagSystem>,
    #[allow(dead_code)]
    storage: Option<Arc<SurrealDbClient>>,
    #[allow(dead_code)]
    conversation_id: Option<String>,
    #[allow(dead_code)]
    results: HashMap<String, serde_json::Value>,
    mode: AiBackendMode,
}

impl AiBackend {
    /// Create simple backend (no RAG, no storage)
    pub fn new_simple(llm: Arc<dyn LlmProvider>) -> Self {
        Self {
            llm,
            rag: None,
            storage: None,
            conversation_id: None,
            results: HashMap::new(),
            mode: AiBackendMode::Interactive,
        }
    }

    /// Create full backend with RAG + storage
    pub fn new_full(
        llm: Arc<dyn LlmProvider>,
        rag: RagSystem,
        storage: Arc<SurrealDbClient>,
    ) -> Self {
        Self {
            llm,
            rag: Some(rag),
            storage: Some(storage),
            conversation_id: None,
            results: HashMap::new(),
            mode: AiBackendMode::Interactive,
        }
    }

    /// Set interaction mode
    pub fn with_mode(mut self, mode: AiBackendMode) -> Self {
        self.mode = mode;
        self
    }
}

#[async_trait]
impl FormBackend for AiBackend {
    async fn initialize(&mut self) -> Result<()> {
        // Create conversation if storage available
        if let Some(ref storage) = self.storage {
            let conv_id = storage
                .create_conversation("ai-form")
                .await
                .map_err(|e| typedialog_core::error::ErrorWrapper::new(e.to_string()))?;
            self.conversation_id = Some(conv_id);
        }

        println!("🤖 AI Backend initialized");
        Ok(())
    }

    async fn render_display_item(
        &self,
        item: &DisplayItem,
        _context: &RenderContext,
    ) -> Result<()> {
        // Simple display rendering
        if let Some(content) = &item.content {
            println!("{}", content);
        }
        Ok(())
    }

    #[allow(clippy::result_large_err)]
    async fn execute_field(
        &self,
        field: &FieldDefinition,
        context: &RenderContext,
    ) -> Result<serde_json::Value> {
        println!("\n🔍 Analyzing field: {}", field.prompt);

        // NOTE: RAG context would require mutable access to self.rag
        // For now we skip RAG in the interactive flow
        // In production, use RefCell<RagSystem> or Arc<Mutex<RagSystem>>
        let rag_context: Option<Vec<_>> = None;

        // Build prompt for LLM
        let prompt = self.build_field_prompt(field, context, rag_context.as_ref());

        let messages = vec![
            Message::system("You are a configuration assistant. Suggest optimal field values."),
            Message::user(&prompt),
        ];

        // Get LLM suggestion
        let suggestion = self
            .llm
            .generate(&messages, &GenerationOptions::default())
            .await
            .map_err(|e| typedialog_core::error::ErrorWrapper::new(e.to_string()))?;

        // Display suggestion and get user input
        println!(
            "💡 AI Suggestion: {}",
            suggestion.lines().next().unwrap_or(&suggestion)
        );

        match self.mode {
            AiBackendMode::Interactive => {
                // Interactive: show suggestion, let user override
                use dialoguer::Input;
                let value: String = Input::new()
                    .with_prompt(format!("{} (press Enter for suggestion)", field.prompt))
                    .default(suggestion.lines().next().unwrap_or("").to_string())
                    .interact_text()
                    .map_err(|e| typedialog_core::error::ErrorWrapper::new(e.to_string()))?;

                Ok(self.parse_value(&value, field)?)
            }
            AiBackendMode::AutoComplete => {
                // AutoComplete: use suggestion directly
                println!("✓ Using AI suggestion");
                Ok(self.parse_value(suggestion.lines().next().unwrap_or(""), field)?)
            }
            AiBackendMode::ValidateOnly => {
                // ValidateOnly: ask user first, then validate with LLM
                use dialoguer::Input;
                let value: String = Input::new()
                    .with_prompt(&field.prompt)
                    .interact_text()
                    .map_err(|e| typedialog_core::error::ErrorWrapper::new(e.to_string()))?;

                // Validate with LLM
                let validation_msg = format!(
                    "The user provided '{}' for field '{}'. Is this a valid value? Respond with YES or NO.",
                    value, field.name
                );
                let validation_response = self
                    .llm
                    .generate(
                        &[Message::user(&validation_msg)],
                        &GenerationOptions::default(),
                    )
                    .await
                    .map_err(|e| typedialog_core::error::ErrorWrapper::new(e.to_string()))?;

                if validation_response.to_uppercase().contains("YES") {
                    println!("✓ Value validated");
                } else {
                    println!("⚠ Warning: LLM suggests this might not be ideal");
                }

                Ok(self.parse_value(&value, field)?)
            }
        }
    }

    async fn shutdown(&mut self) -> Result<()> {
        println!("🤖 AI Backend shutdown");
        Ok(())
    }

    fn is_available() -> bool {
        // AI requires API keys
        std::env::var("OPENAI_API_KEY").is_ok()
            || std::env::var("ANTHROPIC_API_KEY").is_ok()
            || std::env::var("OLLAMA_API_URL").is_ok()
    }

    fn name(&self) -> &str {
        "ai"
    }

    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

impl AiBackend {
    fn build_field_prompt(
        &self,
        field: &FieldDefinition,
        context: &RenderContext,
        rag: Option<&Vec<typedialog_core::ai::rag::RetrievalResult>>,
    ) -> String {
        let mut prompt = String::new();

        // Previous values context
        if !context.results.is_empty() {
            prompt.push_str("## Current Configuration\n");
            for (k, v) in context.results.iter() {
                prompt.push_str(&format!("- {}: {}\n", k, v));
            }
            prompt.push('\n');
        }

        // RAG examples
        if let Some(results) = rag {
            if !results.is_empty() {
                prompt.push_str("## Relevant Examples\n");
                for res in results.iter().take(3) {
                    prompt.push_str(&format!(
                        "- {}\n",
                        res.content.lines().next().unwrap_or(&res.content)
                    ));
                }
                prompt.push('\n');
            }
        }

        // Field info
        prompt.push_str(&format!(
            "## Field to Configure\n\
             Name: {}\n\
             Type: {:?}\n\
             Description: {}\n\
             \n\
             Suggest an appropriate value:",
            field.name, field.field_type, field.prompt
        ));

        prompt
    }

    #[allow(clippy::result_large_err)]
    fn parse_value(&self, value: &str, field: &FieldDefinition) -> Result<serde_json::Value> {
        match field.field_type {
            FieldType::Text | FieldType::Password | FieldType::Editor | FieldType::Select => {
                Ok(json!(value))
            }
            FieldType::Confirm => {
                let is_true = matches!(
                    value.to_lowercase().as_str(),
                    "true" | "yes" | "y" | "1" | "on"
                );
                Ok(json!(is_true))
            }
            FieldType::Custom
            | FieldType::Date
            | FieldType::MultiSelect
            | FieldType::RepeatingGroup => {
                // Try to parse as JSON first, fallback to string
                serde_json::from_str(value).or_else(|_| Ok(json!(value)))
            }
        }
    }
}
