//! Rustelo Page Generator Library
//!
//! This crate provides tools for generating new pages in the Rustelo framework,
//! including interactive TUI interfaces, code generation, and project integration.

use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use rustelo_core_types::i18n::discover_available_languages;

pub mod config;
pub mod generator;
pub mod generation;
pub mod editor;
pub mod page_manager;
pub mod version_control;

// TUI modules (always available now)
pub mod app;
pub mod ui;

// Dashboard system (new comprehensive TUI)
pub mod dashboard;
pub mod input;

/// Available page templates
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PageTemplate {
    BasicPage,
    HeroPage,
    FormPage,
    ContentList,
}

impl PageTemplate {
    /// Get all available templates
    pub fn all() -> Vec<Self> {
        vec![
            Self::BasicPage,
            Self::HeroPage,
            Self::FormPage,
            Self::ContentList,
        ]
    }

    /// Get template as string for TOML config
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::BasicPage => "basic_page",
            Self::HeroPage => "hero_page",
            Self::FormPage => "form_page",
            Self::ContentList => "content_list",
        }
    }

    /// Get human-readable description
    pub fn description(&self) -> &'static str {
        match self {
            Self::BasicPage => "Basic page with title and content",
            Self::HeroPage => "Hero section with CTA buttons",
            Self::FormPage => "Page with form elements",
            Self::ContentList => "List of content items with filtering",
        }
    }
}

impl std::fmt::Display for PageTemplate {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let name = match self {
            Self::BasicPage => "Basic Page",
            Self::HeroPage => "Hero Page",
            Self::FormPage => "Form Page",
            Self::ContentList => "Content List",
        };
        write!(f, "{}", name)
    }
}

/// Configuration for generating a new page
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageConfig {
    /// Page name in PascalCase (e.g. "AwesomeFeature")
    pub name: String,
    /// Module name in snake_case (e.g. "awesome_feature")
    pub module_name: String,
    /// Template type
    pub template: PageTemplate,
    /// Languages to generate for
    pub languages: Vec<String>,
    /// Route path (e.g. "/awesome-feature")
    pub route_path: String,
    /// Priority for routing (default 1.0)
    pub priority: f64,
    /// Whether to auto-scaffold files
    pub auto_scaffold: bool,
    /// I18n patterns for translation keys
    pub i18n_patterns: Vec<String>,
    /// Replace existing boilerplate
    pub replace_existing: bool,
}

impl PageConfig {
    /// Create a new page config with defaults
    pub fn new(name: String) -> Result<Self> {
        let module_name = Self::name_to_module(&name)?;
        let route_path = format!("/{}", module_name.replace('_', "-"));
        let i18n_patterns = vec![format!("{}-", module_name.replace('_', "-"))];

        // Use discovered languages instead of hardcoding
        let discovered_languages = discover_available_languages();
        let default_languages = if discovered_languages.is_empty() {
            vec!["en".to_string()]
        } else {
            discovered_languages.clone()
        };

        Ok(Self {
            name: name.clone(),
            module_name,
            template: PageTemplate::BasicPage,
            languages: default_languages,
            route_path,
            priority: 1.0,
            auto_scaffold: true,
            i18n_patterns,
            replace_existing: false,
        })
    }

    /// Convert PascalCase name to snake_case module name
    pub fn name_to_module(name: &str) -> Result<String> {
        if name.is_empty() {
            anyhow::bail!("Page name cannot be empty");
        }

        // Convert PascalCase to snake_case
        let mut result = String::new();
        let mut chars = name.chars().peekable();

        while let Some(c) = chars.next() {
            if c.is_uppercase() && !result.is_empty() {
                result.push('_');
            }
            result.push(c.to_lowercase().next().unwrap_or(c));
        }

        // Validate the result
        if !result.chars().all(|c| c.is_ascii_lowercase() || c == '_' || c.is_ascii_digit()) {
            anyhow::bail!("Invalid page name: '{}'. Use PascalCase with ASCII letters and numbers only.", name);
        }

        Ok(result)
    }

    /// Validate the configuration
    pub fn validate(&self) -> Result<()> {
        if self.name.is_empty() {
            anyhow::bail!("Page name cannot be empty");
        }

        if self.languages.is_empty() {
            anyhow::bail!("At least one language must be selected");
        }

        if self.route_path.is_empty() || !self.route_path.starts_with('/') {
            anyhow::bail!("Route path must start with '/'");
        }

        if self.i18n_patterns.is_empty() {
            anyhow::bail!("At least one i18n pattern must be provided");
        }

        Ok(())
    }
}

/// Discover available languages using shared infrastructure (language-agnostic)
pub fn discover_languages(_project_root: &Path) -> Result<Vec<String>> {
    // Use the shared crate's language discovery which is configuration-driven
    let discovered_languages = discover_available_languages();

    if discovered_languages.is_empty() {
        // Fallback to environment variable or "en"
        let fallback_lang = std::env::var("DEFAULT_LANGUAGE").unwrap_or_else(|_| "en".to_string());
        Ok(vec![fallback_lang])
    } else {
        Ok(discovered_languages.clone())
    }
}

/// Find project root by looking for Cargo.toml
pub fn find_project_root() -> Result<PathBuf> {
    let mut current = std::env::current_dir()?;

    loop {
        if current.join("Cargo.toml").exists() {
            return Ok(current);
        }

        if let Some(parent) = current.parent() {
            current = parent.to_path_buf();
        } else {
            anyhow::bail!("Could not find project root (no Cargo.toml found)");
        }
    }
}

// Removed separate process launching - everything now runs in same app

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

    #[test]
    fn test_name_to_module() {
        assert_eq!(PageConfig::name_to_module("Home").unwrap(), "home");
        assert_eq!(PageConfig::name_to_module("AwesomeFeature").unwrap(), "awesome_feature");
        assert_eq!(PageConfig::name_to_module("TestPage123").unwrap(), "test_page123");
    }

    #[test]
    fn test_invalid_names() {
        assert!(PageConfig::name_to_module("").is_err());
        assert!(PageConfig::name_to_module("test-page").is_err());
        assert!(PageConfig::name_to_module("test page").is_err());
    }

    #[test]
    fn test_page_config_creation() {
        let config = PageConfig::new("TestPage".to_string()).unwrap();
        assert_eq!(config.name, "TestPage");
        assert_eq!(config.module_name, "test_page");
        assert_eq!(config.route_path, "/test-page");
        assert_eq!(config.i18n_patterns, vec!["test-page-"]);
    }
}
