//! Route configuration loading with NCL and TOML support
//!
//! This module provides typed route configuration loading from both Nickel (NCL)
//! and TOML files, using the framework's `RouteConfigToml` types and `rustelo_config`
//! for NCL processing.
//!
//! # Architecture
//!
//! - **Structs**: Uses `rustelo_core_types::routing::RouteConfigToml` (framework)
//! - **NCL loading**: Delegates to `rustelo_config` (framework tool)
//! - **Auto-detection**: .ncl preferred, .toml fallback
//!
//! # Examples
//!
//! ```no_run
//! use build_config::route_config;
//! use std::path::Path;
//!
//! // Load all language route files
//! let routes_dir = Path::new("site/config/routes");
//! let all_routes = route_config::load_all_routes(routes_dir)?;
//!
//! for (lang, routes) in &all_routes {
//!     println!("Language {}: {} routes", lang, routes.len());
//!     for route in routes {
//!         println!("  {} -> {}", route.path, route.component);
//!     }
//! }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```

use anyhow::{Context, Result};
use rustelo_core_types::routing::{RouteConfigToml, RoutesConfig};
use std::collections::HashMap;
use std::fs;
use std::path::Path;

/// Load routes from a TOML file with typed deserialization
///
/// Reads a TOML file containing route configuration and deserializes it into
/// a vector of `RouteConfigToml` structs.
///
/// # File Format
///
/// Expected TOML structure:
///
/// ```toml
/// [[routes]]
/// component = "Home"
/// path = "/"
/// language = "en"
/// enabled = true
/// title_key = "home-page-title"
/// description_key = "home-page-description"
/// # ... other fields
/// ```
///
/// # Errors
///
/// Returns error if:
/// - File cannot be read
/// - TOML syntax is invalid
/// - Deserialization fails (missing required fields, wrong types)
///
/// # Examples
///
/// ```no_run
/// use build_config::route_config::load_routes_from_toml;
/// use std::path::Path;
///
/// let path = Path::new("site/config/routes/en.toml");
/// let routes = load_routes_from_toml(path)?;
/// println!("Loaded {} routes", routes.len());
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn load_routes_from_toml(path: &Path) -> Result<Vec<RouteConfigToml>> {
    let content = fs::read_to_string(path)
        .with_context(|| format!("Failed to read TOML file: {}", path.display()))?;

    let routes_config: RoutesConfig = toml::from_str(&content)
        .with_context(|| format!("Failed to parse TOML file: {}", path.display()))?;

    Ok(routes_config.routes)
}

/// Load routes from a Nickel (NCL) file via rustelo_config
///
/// Uses `rustelo_config` to export NCL to JSON, then deserializes into typed structs.
/// This provides schema validation, defaults, and type contracts from NCL.
///
/// # NCL Pipeline
///
/// ```text
/// .ncl file
///     ↓
/// nickel export --format json (subprocess)
///     ↓
/// JSON string
///     ↓
/// serde_json::from_str
///     ↓
/// RouteConfigToml (typed)
/// ```
///
/// # Errors
///
/// Returns error if:
/// - Nickel CLI is not installed (`nickel` command not found)
/// - NCL export fails (syntax error, contract violation)
/// - JSON deserialization fails (schema mismatch)
///
/// # Examples
///
/// ```no_run
/// use build_config::route_config::load_routes_from_ncl;
/// use std::path::Path;
///
/// let path = Path::new("site/config/routes/en.ncl");
/// let routes = load_routes_from_ncl(path)?;
/// println!("Loaded {} routes from NCL", routes.len());
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn load_routes_from_ncl(path: &Path) -> Result<Vec<RouteConfigToml>> {
    // Use rustelo_config to export NCL to JSON
    let json_string = rustelo_config::nickel::export_to_json(path)
        .with_context(|| format!("Failed to export NCL file: {}", path.display()))?;

    // Deserialize JSON to RoutesConfig
    let routes_config: RoutesConfig = serde_json::from_str(&json_string).with_context(|| {
        format!(
            "Failed to deserialize routes from JSON (NCL export): {}",
            path.display()
        )
    })?;

    Ok(routes_config.routes)
}

/// Load routes with format auto-detection (.ncl preferred, .toml fallback)
///
/// Tries to load from NCL first, falls back to TOML if:
/// - NCL file doesn't exist
/// - Nickel CLI is not available
/// - NCL export fails
///
/// This allows gradual migration from TOML to NCL without breaking builds.
///
/// # Format Detection Order
///
/// 1. Check if `.ncl` file exists → load via `load_routes_from_ncl()`
/// 2. Check if `nickel` CLI available → try NCL
/// 3. Fallback to `.toml` → load via `load_routes_from_toml()`
///
/// # Errors
///
/// Returns error only if BOTH formats fail:
/// - NCL file exists but export fails AND TOML file doesn't exist
/// - Neither NCL nor TOML file exists
///
/// # Examples
///
/// ```no_run
/// use build_config::route_config::load_routes;
/// use std::path::Path;
///
/// // Will try en.ncl first, fallback to en.toml
/// let path = Path::new("site/config/routes/en");
/// let routes = load_routes(path)?;
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn load_routes(base_path: &Path) -> Result<Vec<RouteConfigToml>> {
    // Try NCL first (preferred)
    let ncl_path = base_path.with_extension("ncl");
    if ncl_path.exists() && rustelo_config::is_nickel_available() {
        match load_routes_from_ncl(&ncl_path) {
            Ok(routes) => return Ok(routes),
            Err(e) => {
                eprintln!(
                    "Warning: NCL loading failed for {}, falling back to TOML: {}",
                    ncl_path.display(),
                    e
                );
            }
        }
    }

    // Fallback to TOML
    let toml_path = base_path.with_extension("toml");
    if toml_path.exists() {
        return load_routes_from_toml(&toml_path);
    }

    anyhow::bail!(
        "No route configuration found at {} (.ncl or .toml)",
        base_path.display()
    )
}

/// Load all language route files from a directory
///
/// Scans a directory for route configuration files (both .ncl and .toml),
/// and loads them into a HashMap keyed by language code extracted from filename.
///
/// # File Naming Convention
///
/// Expected filename pattern: `{language}.{ncl|toml}`
///
/// Examples:
/// - `en.ncl` → language code "en"
/// - `es.toml` → language code "es"
/// - `en.ncl` + `en.toml` → prefers .ncl
///
/// # Returns
///
/// HashMap where:
/// - Key: Language code (e.g., "en", "es", "fr")
/// - Value: Vector of routes for that language
///
/// # Errors
///
/// Returns error if:
/// - Directory doesn't exist or can't be read
/// - Any route file fails to load (after trying both NCL and TOML)
///
/// # Examples
///
/// ```no_run
/// use build_config::route_config::load_all_routes;
/// use std::path::Path;
///
/// let routes_dir = Path::new("site/config/routes");
/// let all_routes = load_all_routes(routes_dir)?;
///
/// for (lang, routes) in &all_routes {
///     println!("Language: {}, Routes: {}", lang, routes.len());
/// }
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
pub fn load_all_routes(routes_dir: &Path) -> Result<HashMap<String, Vec<RouteConfigToml>>> {
    let mut all_routes = HashMap::new();

    if !routes_dir.exists() {
        anyhow::bail!("Routes directory does not exist: {}", routes_dir.display());
    }

    // Scan directory for route files
    let entries = fs::read_dir(routes_dir)
        .with_context(|| format!("Failed to read routes directory: {}", routes_dir.display()))?;

    let mut base_names = std::collections::HashSet::new();

    // Collect all base names (without extension)
    for entry in entries {
        let entry = entry?;
        let path = entry.path();

        // Skip directories
        if path.is_dir() {
            continue;
        }

        // Check if it's a route file (.ncl or .toml)
        if let Some(ext) = path.extension() {
            if ext == "ncl" || ext == "toml" {
                if let Some(base_name) = path.file_stem().and_then(|s| s.to_str()) {
                    base_names.insert(base_name.to_string());
                }
            }
        }
    }

    // Load routes for each language (base name)
    for lang in base_names {
        let base_path = routes_dir.join(&lang);
        match load_routes(&base_path) {
            Ok(routes) => {
                all_routes.insert(lang.clone(), routes);
            }
            Err(e) => {
                eprintln!(
                    "Warning: Failed to load routes for language '{}': {}",
                    lang, e
                );
                // Continue loading other languages instead of failing completely
            }
        }
    }

    if all_routes.is_empty() {
        anyhow::bail!(
            "No route files found in directory: {}",
            routes_dir.display()
        );
    }

    Ok(all_routes)
}

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

    #[test]
    fn test_load_routes_requires_existing_file() {
        let fake_path = PathBuf::from("/nonexistent/routes.toml");
        let result = load_routes_from_toml(&fake_path);
        assert!(result.is_err());
    }

    #[test]
    fn test_auto_detection_prefers_ncl() {
        // This test would require actual fixture files
        // For now, just verify the function signature
        let base_path = PathBuf::from("site/config/routes/en");
        let _ = load_routes(&base_path);
    }
}
