//! Form rendering backends abstraction
//!
//! This module provides a trait-based abstraction for different form rendering
//! backends (CLI with inquire, TUI with ratatui, Web with axum, etc.).

use crate::error::Result;
use crate::form_parser::{DisplayItem, FieldDefinition};
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;

/// Context passed to rendering operations
///
/// `results` is reference-counted: cloning a `RenderContext` (or creating a new one
/// from an existing accumulator via `Arc::clone`) costs a single atomic increment
/// instead of a full `HashMap` deep clone.
#[derive(Debug, Clone)]
pub struct RenderContext {
    /// Accumulated field results from previous form steps (read-only for backends)
    pub results: Arc<HashMap<String, Value>>,
    /// Optional locale override
    pub locale: Option<String>,
}

/// Trait for form rendering backends
///
/// Implementations handle the rendering of display items and execution of form fields.
/// Different backends can target different environments (CLI, TUI, Web, etc.).
#[async_trait]
pub trait FormBackend: Send + Sync {
    /// Initialize the backend (setup terminal, start server, etc.)
    async fn initialize(&mut self) -> Result<()>;

    /// Render a display item (header, section, border, footer, etc.)
    async fn render_display_item(&self, item: &DisplayItem, context: &RenderContext) -> Result<()>;

    /// Execute a field and return user input (field-by-field mode)
    async fn execute_field(
        &self,
        field: &FieldDefinition,
        context: &RenderContext,
    ) -> Result<Value>;

    /// Execute complete form with all fields at once (complete mode)
    ///
    /// Takes owned items/fields for mutable rendering operations.
    /// Form and base_dir references are provided for reactive fragment loading.
    /// initial_values provides pre-populated field values (e.g., from --defaults).
    ///
    /// Returns all field values as a map
    async fn execute_form_complete(
        &mut self,
        form: &crate::form_parser::FormDefinition,
        _base_dir: &Path,
        items: Vec<DisplayItem>,
        fields: Vec<FieldDefinition>,
        initial_values: Option<HashMap<String, Value>>,
    ) -> Result<std::collections::HashMap<String, Value>> {
        // Default implementation: fall back to field-by-field mode.
        // The Arc accumulator makes context construction O(1) (atomic ref-count increment)
        // instead of O(n) (HashMap clone). Arc::make_mut is O(1) when refcount == 1,
        // which is guaranteed because each context is dropped before the next insert.
        let mut results: Arc<HashMap<String, Value>> = Arc::new(initial_values.unwrap_or_default());
        let locale = form.locale.clone();

        for item in &items {
            let context = RenderContext {
                results: Arc::clone(&results),
                locale: locale.clone(),
            };
            self.render_display_item(item, &context).await?;
        }

        for field in &fields {
            let context = RenderContext {
                results: Arc::clone(&results),
                locale: locale.clone(),
            };
            let value = self.execute_field(field, &context).await?;
            Arc::make_mut(&mut results).insert(field.name.clone(), value);
        }

        Ok(Arc::try_unwrap(results).unwrap_or_else(|arc| (*arc).clone()))
    }

    /// Cleanup/shutdown the backend
    async fn shutdown(&mut self) -> Result<()>;

    /// Downcast to concrete type (for accessing backend-specific features)
    fn as_any(&self) -> &dyn std::any::Any;

    /// Check if this backend is available on the current system
    fn is_available() -> bool
    where
        Self: Sized;

    /// Get the backend name (for logging/debugging)
    fn name(&self) -> &str;
}

/// Backend type enumeration for factory pattern
#[derive(Debug, Clone, Copy)]
pub enum BackendType {
    /// CLI backend using inquire
    Cli,
    /// TUI backend using ratatui
    #[cfg(feature = "tui")]
    Tui,
    /// Web backend using axum
    #[cfg(feature = "web")]
    Web { port: u16 },
}

/// Factory for creating backend instances
pub struct BackendFactory;

impl BackendFactory {
    /// Create a backend instance based on the provided type
    pub fn create(backend_type: BackendType) -> Result<Box<dyn FormBackend>> {
        match backend_type {
            BackendType::Cli => {
                #[cfg(feature = "cli")]
                {
                    Ok(Box::new(cli::InquireBackend::new()))
                }
                #[cfg(not(feature = "cli"))]
                {
                    Err(crate::ErrorWrapper::new(
                        crate::error::ErrorKind::Other,
                        "CLI backend not enabled. Compile with --features cli",
                    ))
                }
            }
            #[cfg(feature = "tui")]
            BackendType::Tui => Ok(Box::new(tui::RatatuiBackend::new())),
            #[cfg(feature = "web")]
            BackendType::Web { port } => Ok(Box::new(web::WebBackend::new(port))),
        }
    }

    /// Auto-detect the best available backend
    pub fn auto_detect() -> BackendType {
        // Check environment variable override
        if let Ok(backend) = std::env::var("TYPEDIALOG_BACKEND") {
            match backend.to_lowercase().as_str() {
                "tui" => {
                    #[cfg(feature = "tui")]
                    return BackendType::Tui;
                    #[cfg(not(feature = "tui"))]
                    {}
                }
                "web" => {
                    #[cfg(feature = "web")]
                    {
                        let port = std::env::var("TYPEDIALOG_PORT")
                            .ok()
                            .and_then(|p| p.parse::<u16>().ok())
                            .unwrap_or(9000);
                        return BackendType::Web { port };
                    }
                    #[cfg(not(feature = "web"))]
                    {}
                }
                _ => {}
            }
        }

        // Default to CLI backend
        BackendType::Cli
    }
}

/// CLI Backend - using inquire for interactive prompts
#[cfg(feature = "cli")]
pub mod cli;

/// TUI Backend - using ratatui for terminal UI
#[cfg(feature = "tui")]
pub mod tui;

/// Web Backend - using axum for web forms
#[cfg(feature = "web")]
pub mod web;

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

    #[test]
    fn test_backend_type_debug() {
        let backend = BackendType::Cli;
        let debug_str = format!("{:?}", backend);
        assert!(debug_str.contains("Cli"));
    }

    #[test]
    fn test_render_context_clone() {
        let ctx = RenderContext {
            results: Arc::new(HashMap::new()),
            locale: Some("en-US".to_string()),
        };
        let cloned = ctx.clone();
        assert_eq!(cloned.locale, ctx.locale);
        // Both point to the same allocation
        assert!(Arc::ptr_eq(&ctx.results, &cloned.results));
    }
}
