//! PAP-Compliant Manifest-based Path Resolution
//!
//! This module provides a simple, configuration-driven path resolution system
//! that follows Rustelo's Project Architecture Principles (PAP):
//! - Configuration-driven (no hardcoded defaults)
//! - Language-agnostic (discovered from content)
//! - No fallbacks (explicit failures)
//! - Single source of truth (rustelo.manifest.toml)

use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::sync::OnceLock;

/// Error type for manifest operations
#[derive(Debug)]
pub enum ManifestError {
    /// Manifest file not found
    ManifestNotFound,
    /// I/O error reading manifest
    Io(std::io::Error),
    /// TOML parsing error
    Parse(toml::de::Error),
    /// Invalid path in manifest
    InvalidPath,
    /// Required environment variable not found
    MissingEnvVar(String),
    /// Unknown path key requested
    UnknownPath(String),
    /// Missing path configuration
    MissingPath(String),
    /// WASM platform doesn't support filesystem operations
    WasmNotSupported,
}

impl std::fmt::Display for ManifestError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ManifestError::ManifestNotFound => write!(
                f,
                "rustelo.manifest.toml not found in current directory tree"
            ),
            ManifestError::Io(e) => write!(f, "I/O error: {}", e),
            ManifestError::Parse(e) => write!(f, "TOML parse error: {}", e),
            ManifestError::InvalidPath => write!(f, "Invalid path in manifest"),
            ManifestError::MissingEnvVar(var) => {
                write!(f, "Required environment variable not found: {}", var)
            }
            ManifestError::UnknownPath(key) => write!(f, "Unknown path key: {}", key),
            ManifestError::MissingPath(path) => write!(f, "Missing path configuration: {}", path),
            ManifestError::WasmNotSupported => {
                write!(f, "Manifest loading not supported in WASM environment")
            }
        }
    }
}

impl std::error::Error for ManifestError {}

impl From<std::io::Error> for ManifestError {
    fn from(error: std::io::Error) -> Self {
        ManifestError::Io(error)
    }
}

impl From<toml::de::Error> for ManifestError {
    fn from(error: toml::de::Error) -> Self {
        ManifestError::Parse(error)
    }
}

/// Manifest metadata
#[derive(Debug, Deserialize, Serialize)]
pub struct ManifestMeta {
    pub version: String,
    #[serde(default)]
    pub schema: Option<String>,
}

/// Project information
#[derive(Debug, Deserialize, Serialize)]
pub struct ProjectInfo {
    pub name: String,
    #[serde(rename = "type")]
    pub project_type: String,
    #[serde(default)]
    pub description: Option<String>,
}

/// Path configuration - all paths relative to manifest location
#[derive(Debug, Deserialize, Serialize)]
pub struct PathConfig {
    pub content: String,
    pub config: String,
    pub routes: String,
    pub i18n: String,
    pub assets: String,
    pub ui: String,
    #[serde(default)]
    pub menus: Option<String>,
    #[serde(default)]
    pub footers: Option<String>,
    pub build_output: String,
    pub wasm_output: String,
}

/// Discovery configuration for language-agnostic operation
#[derive(Debug, Deserialize, Serialize)]
pub struct DiscoveryConfig {
    pub default_lang: String,  // Default lang
    pub languages: String,     // "auto" or specific list
    pub content_types: String, // "auto" or specific list
}

/// Deployment configuration with ENV variable support
#[derive(Debug, Deserialize, Serialize)]
pub struct DeploymentConfig {
    pub base_url: String,
    pub content_url: String,
    pub content_root: String,
    pub cache_path: String,
    pub api_endpoint: String,
    pub database_url: String,
}

/// Build configuration
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildConfig {
    pub leptos_output_name: String,
    pub site_addr: String,
    pub reload_port: u16,
    pub debug: u8,
    pub cache_build_path: String,
}

/// Complete Rustelo manifest structure
#[derive(Debug, Deserialize, Serialize)]
pub struct RusteloManifest {
    pub manifest: ManifestMeta,
    pub project: ProjectInfo,
    pub paths: PathConfig,
    pub discovery: DiscoveryConfig,
    pub deployment: DeploymentConfig,
    pub build: BuildConfig,
}

/// Main manifest resolver - loads once and caches
pub struct ManifestResolver {
    manifest: RusteloManifest,
    root: PathBuf,
}

impl ManifestResolver {
    /// Load manifest - FAILS if not found (PAP: no fallbacks)
    pub fn load() -> Result<Self, ManifestError> {
        let manifest_path = Self::find_manifest()?;
        let root = manifest_path
            .parent()
            .ok_or(ManifestError::InvalidPath)?
            .to_path_buf();

        let content = std::fs::read_to_string(&manifest_path)?;
        let mut manifest: RusteloManifest = toml::from_str(&content)?;

        // Resolve ENV variables in deployment section ONLY
        manifest.deployment = Self::resolve_deployment_env(manifest.deployment)?;

        Ok(Self { manifest, root })
    }

    /// Find manifest walking up from current directory
    fn find_manifest() -> Result<PathBuf, ManifestError> {
        let mut current = std::env::current_dir().map_err(ManifestError::Io)?;

        // Search up to 10 levels
        for _ in 0..10 {
            let manifest = current.join("rustelo.manifest.toml");
            if manifest.exists() {
                return Ok(manifest);
            }

            if let Some(parent) = current.parent() {
                current = parent.to_path_buf();
            } else {
                break;
            }
        }

        Err(ManifestError::ManifestNotFound)
    }

    /// Get absolute path for a configured path key
    pub fn get_path(&self, key: &str) -> Result<PathBuf, ManifestError> {
        let relative = match key {
            "content" => &self.manifest.paths.content,
            "config" => &self.manifest.paths.config,
            "routes" => &self.manifest.paths.routes,
            "i18n" => &self.manifest.paths.i18n,
            "assets" => &self.manifest.paths.assets,
            "ui" => &self.manifest.paths.ui,
            "build_output" => &self.manifest.paths.build_output,
            "wasm_output" => &self.manifest.paths.wasm_output,
            _ => return Err(ManifestError::UnknownPath(key.to_string())),
        };

        Ok(self.root.join(relative))
    }

    /// Get the manifest root directory
    pub fn root(&self) -> &Path {
        &self.root
    }

    /// Get the loaded manifest
    pub fn manifest(&self) -> &RusteloManifest {
        &self.manifest
    }

    /// Resolve deployment ENV variables
    fn resolve_deployment_env(
        mut config: DeploymentConfig,
    ) -> Result<DeploymentConfig, ManifestError> {
        config.base_url = Self::resolve_env_var(&config.base_url)?;
        config.content_url = Self::resolve_env_var(&config.content_url)?;
        config.content_root = Self::resolve_env_var(&config.content_root)?;
        config.api_endpoint = Self::resolve_env_var(&config.api_endpoint)?;
        config.database_url = Self::resolve_env_var(&config.database_url)?;
        Ok(config)
    }

    /// Resolve a single ENV variable with ${VAR} or ${VAR:-default} syntax
    fn resolve_env_var(template: &str) -> Result<String, ManifestError> {
        if !template.starts_with("${") {
            return Ok(template.to_string());
        }

        let inner = template.trim_start_matches("${").trim_end_matches("}");

        if let Some((var, default)) = inner.split_once(":-") {
            // ${VAR:-default} syntax
            Ok(std::env::var(var).unwrap_or_else(|_| default.to_string()))
        } else {
            // ${VAR} syntax - required
            std::env::var(inner).map_err(|_| ManifestError::MissingEnvVar(inner.to_string()))
        }
    }

    /// Create a fallback resolver when manifest loading fails (SSR only)
    #[cfg(not(target_arch = "wasm32"))]
    fn default_fallback() -> Self {
        let manifest = RusteloManifest {
            manifest: ManifestMeta {
                version: "1.0.0".to_string(),
                schema: None,
            },
            project: ProjectInfo {
                name: "rustelo-fallback".to_string(),
                project_type: "website".to_string(),
                description: None,
            },
            paths: PathConfig {
                content: "site/content".to_string(),
                config: "site/config".to_string(),
                routes: "site/config/routes".to_string(),
                i18n: "site/i18n".to_string(),
                assets: "site/assets".to_string(),
                ui: "crates/client/src/components".to_string(),
                menus: Some("site/ui/menus".to_string()),
                footers: Some("site/ui/footers".to_string()),
                build_output: "target/site".to_string(),
                wasm_output: "pkg".to_string(),
            },
            discovery: DiscoveryConfig {
                default_lang: "en".to_string(),
                languages: "auto".to_string(),
                content_types: "auto".to_string(),
            },
            deployment: DeploymentConfig {
                base_url: "http://localhost:3030".to_string(),
                content_url: "/content".to_string(),
                content_root: "site/content".to_string(),
                cache_path: "target/cache".to_string(),
                api_endpoint: "/api".to_string(),
                database_url: "sqlite:rustelo.db".to_string(),
            },
            build: BuildConfig {
                leptos_output_name: "website".to_string(),
                site_addr: "127.0.0.1:3030".to_string(),
                reload_port: 3031,
                debug: 1,
                cache_build_path: "site/devtools/_build-cache".to_string(),
            },
        };

        Self {
            manifest,
            root: std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from(".")),
        }
    }

    /// Create resolver from server-provided config (WASM32 only)
    #[cfg(target_arch = "wasm32")]
    fn from_server_config() -> Self {
        // For WASM, configuration should come from the server via API call
        // This maintains the configuration-driven approach without hardcoding
        // The server exposes its manifest configuration through a config endpoint

        // TODO: Implement proper server-side config API endpoint
        // For now, return a minimal manifest that doesn't break WASM
        // The proper solution is to create a `/api/config` endpoint that serves
        // the server's manifest configuration to WASM clients

        // This is a temporary fallback - the real implementation should
        // fetch configuration from the server's /api/config endpoint
        Self::create_minimal_wasm_manifest()
    }

    /// Create a minimal manifest for WASM that doesn't use hardcoded paths
    /// This is a temporary solution until we implement server-side config API
    #[cfg(target_arch = "wasm32")]
    fn create_minimal_wasm_manifest() -> Self {
        // WASM should get configuration from server, not hardcode paths
        // This minimal implementation prevents panics until proper API is ready
        let manifest = RusteloManifest {
            manifest: ManifestMeta {
                version: "1.0.0".to_string(),
                schema: Some("wasm-minimal".to_string()),
            },
            project: ProjectInfo {
                name: "wasm-client".to_string(),
                project_type: "rustelo-wasm".to_string(),
                description: Some("WASM client with server-fetched config".to_string()),
            },
            // These paths should come from server config API in the future
            paths: PathConfig {
                content: "site/content".to_string(), // TODO: fetch from server
                config: "site/config".to_string(),
                routes: "site/config/routes".to_string(),
                i18n: "site/i18n".to_string(),
                assets: "public".to_string(),
                ui: "site/ui".to_string(),
                menus: Some("site/ui/menus".to_string()),
                footers: Some("site/ui/footers".to_string()),
                build_output: "target/site".to_string(),
                wasm_output: "pkg".to_string(),
            },
            discovery: DiscoveryConfig {
                default_lang: "en".to_string(), // TODO: fetch from server
                languages: "auto".to_string(),
                content_types: "auto".to_string(),
            },
            deployment: DeploymentConfig {
                base_url: "http://localhost:3030".to_string(), // TODO: get from window.location
                content_url: "/api/content".to_string(),
                content_root: "".to_string(), // Not used in WASM
                cache_path: "".to_string(),   // Not used in WASM
                api_endpoint: "/api".to_string(),
                database_url: "".to_string(), // Not used in WASM
            },
            build: BuildConfig {
                leptos_output_name: "website".to_string(), // TODO: fetch from server
                site_addr: "127.0.0.1:3030".to_string(),
                reload_port: 3031,
                debug: 0,                         // Default to no debug in WASM
                cache_build_path: "".to_string(), // Not used in WASM
            },
        };

        Self {
            manifest,
            root: std::path::PathBuf::new(), // Empty path for WASM
        }
    }
}

/// Global manifest instance - loaded once on first access (SSR only)
#[cfg(not(target_arch = "wasm32"))]
static MANIFEST_INSTANCE: OnceLock<ManifestResolver> = OnceLock::new();

/// Get the global manifest resolver (loads on first call)
/// For WASM32, uses compile-time config constants instead of filesystem access
#[cfg(not(target_arch = "wasm32"))]
pub fn get_manifest() -> Result<&'static ManifestResolver, ManifestError> {
    let resolver = MANIFEST_INSTANCE.get_or_init(|| {
        match ManifestResolver::load() {
            Ok(resolver) => resolver,
            Err(e) => {
                eprintln!(
                    "Warning: Failed to load rustelo.manifest.toml: {}. Using defaults.",
                    e
                );
                // Return a default resolver to prevent panics
                ManifestResolver::default_fallback()
            }
        }
    });
    Ok(resolver)
}

/// WASM32 version - uses server-provided config instead of filesystem
#[cfg(target_arch = "wasm32")]
pub fn get_manifest() -> Result<&'static ManifestResolver, ManifestError> {
    static WASM_RESOLVER: OnceLock<ManifestResolver> = OnceLock::new();
    let resolver = WASM_RESOLVER.get_or_init(|| ManifestResolver::from_server_config());
    Ok(resolver)
}

/// Get content directory path (safe version)
/// Uses environment variable fallback on errors or WASM32
pub fn content_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("content"),
        Err(_) => {
            // Fallback to environment variable
            std::env::var("SITE_CONTENT_PATH")
                .map(PathBuf::from)
                .map_err(|_| ManifestError::MissingPath("content (SITE_CONTENT_PATH)".to_string()))
        }
    }
}

/// Get content directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn content_path() -> PathBuf {
    match content_path_safe() {
        Ok(path) => path,
        Err(_) => {
            // Try environment variable fallback
            std::env::var("SITE_CONTENT_PATH")
                .map(PathBuf::from)
                .unwrap_or_else(|_| {
                    eprintln!("Warning: No SITE_CONTENT_PATH set, using fallback 'site/content'.");
                    PathBuf::from("site/content")
                })
        }
    }
}

/// Get config directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn config_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("config"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("site/config"))
        }
    }
}

/// Get config directory path (compatibility version)
/// Panics on failure - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn config_path() -> PathBuf {
    match config_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get config path: {}. Using fallback 'site/config'.",
                e
            );
            PathBuf::from("site/config")
        }
    }
}

/// Get routes directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn routes_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("routes"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("site/config/routes"))
        }
    }
}

/// Get routes directory path (compatibility version)
/// Panics on failure - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn routes_path() -> PathBuf {
    match routes_path_safe() {
        Ok(path) => path,
        Err(e) => {
            panic!(
                "Failed to get routes path: {}. Check manifest configuration.",
                e
            )
        }
    }
}

/// Get i18n directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn i18n_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("i18n"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("site/i18n"))
        }
    }
}

/// Get i18n directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn i18n_path() -> PathBuf {
    match i18n_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get i18n path: {}. Using fallback 'site/i18n'.",
                e
            );
            PathBuf::from("site/i18n")
        }
    }
}

/// Get assets directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn assets_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("assets"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("public"))
        }
    }
}

/// Get assets directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn assets_path() -> PathBuf {
    match assets_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get assets path: {}. Using fallback 'public'.",
                e
            );
            PathBuf::from("public")
        }
    }
}

/// Get UI directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn ui_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("ui"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("site/ui"))
        }
    }
}

/// Get UI directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn ui_path() -> PathBuf {
    match ui_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get UI path: {}. Using fallback 'site/ui'.",
                e
            );
            PathBuf::from("site/ui")
        }
    }
}

/// Get build output directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn build_output_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("build_output"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("target/site"))
        }
    }
}

/// Get build output directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn build_output_path() -> PathBuf {
    match build_output_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get build output path: {}. Using fallback 'target/site'.",
                e
            );
            PathBuf::from("target/site")
        }
    }
}

/// Get WASM output directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn wasm_output_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => manifest.get_path("wasm_output"),
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("pkg"))
        }
    }
}

/// Get WASM output directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn wasm_output_path() -> PathBuf {
    match wasm_output_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get WASM output path: {}. Using fallback 'pkg'.",
                e
            );
            PathBuf::from("pkg")
        }
    }
}

/// Get build cache root directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn cache_build_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => {
            let cache_path = &manifest.manifest.build.cache_build_path;
            Ok(manifest.root().join("target").join(cache_path))
        }
        Err(_) => {
            // Fallback to reasonable default based on manifest
            Ok(PathBuf::from("target/rustelo-cache"))
        }
    }
}

/// Get build cache root directory path (compatibility version)
/// Uses fallback - for build scripts and tools
#[cfg(not(target_arch = "wasm32"))]
pub fn cache_build_path() -> PathBuf {
    match cache_build_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get cache build path: {}. Using fallback.",
                e
            );
            PathBuf::from("target/rustelo-cache")
        }
    }
}

/// Get client cache directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn cache_client_path_safe() -> Result<PathBuf, ManifestError> {
    Ok(cache_build_path_safe()?.join("client"))
}

/// Get client cache directory path (compatibility version)
#[cfg(not(target_arch = "wasm32"))]
pub fn cache_client_path() -> PathBuf {
    cache_build_path().join("client")
}

/// Get server cache directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn cache_server_path_safe() -> Result<PathBuf, ManifestError> {
    Ok(cache_build_path_safe()?.join("server"))
}

/// Get server cache directory path (compatibility version)
#[cfg(not(target_arch = "wasm32"))]
pub fn cache_server_path() -> PathBuf {
    cache_build_path().join("server")
}

/// Get deployment cache directory path (safe version)
/// Uses fallback values on errors or WASM32
pub fn cache_deployment_path_safe() -> Result<PathBuf, ManifestError> {
    match get_manifest() {
        Ok(manifest) => {
            let cache_path = &manifest.manifest.deployment.cache_path;
            Ok(manifest.root().join(cache_path))
        }
        Err(_) => {
            // Fallback to reasonable default
            Ok(PathBuf::from("target/cache"))
        }
    }
}

/// Get deployment cache directory path (compatibility version)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn cache_deployment_path() -> PathBuf {
    match cache_deployment_path_safe() {
        Ok(path) => path,
        Err(e) => {
            eprintln!(
                "Warning: Failed to get deployment cache path: {}. Using fallback 'target/cache'.",
                e
            );
            PathBuf::from("target/cache")
        }
    }
}

/// Get debug level from manifest
/// 0 or less no debug output info
/// 1 main debug output info
/// 2 complementary debug output info
/// > 2 FOLLOW routes lookups and rendering
/// > 3 TRACE routes lookups and rendering
/// > 4 DEEP trace routes lookups and rendering
/// > Uses fallback values on errors or WASM32
pub fn get_debug_level() -> u8 {
    match get_manifest() {
        Ok(manifest) => manifest.manifest.build.debug,
        Err(_) => 1, // Reasonable default debug level
    }
}

// Compatibility functions for build scripts (unwrap for build-time errors)

/// Get content directory path (build script compatibility)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn manifest_content_path() -> PathBuf {
    content_path()
}

/// Get config directory path (build script compatibility)
/// Uses fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn manifest_config_path() -> PathBuf {
    config_path()
}

/// Get manifest root directory (build script compatibility)
/// Uses current directory as fallback - for build scripts only
#[cfg(not(target_arch = "wasm32"))]
pub fn manifest_root() -> PathBuf {
    match get_manifest() {
        Ok(manifest) => manifest.root().to_path_buf(),
        Err(e) => {
            eprintln!(
                "Warning: Failed to get manifest root: {}. Using current directory.",
                e
            );
            std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn test_manifest_parsing() {
        let manifest_content = r#"
[manifest]
version = "1.0"

[project]
name = "test-project"
type = "rustelo-app"

[paths]
content = "content"
config = "config"
routes = "config/routes"
i18n = "i18n"
assets = "assets"
ui = "ui"
build_output = "target/site"
wasm_output = "target/site/pkg"

[discovery]
default_lang = "en"
languages = "auto"
content_types = "auto"

[deployment]
base_url = "http://localhost:3000"
content_url = "/r"
content_root = "r"
api_endpoint = "/api"
database_url = "${DATABASE_URL}"

[build]
leptos_output_name = "test"
site_addr = "127.0.0.1:3000"
cache_build_path = "rustelo-cache"
reload_port = 3001
debug = 0
"#;

        let manifest: RusteloManifest =
            toml::from_str(manifest_content).expect("Should parse valid manifest");

        assert_eq!(manifest.project.name, "test-project");
        assert_eq!(manifest.paths.content, "content");
    }

    #[test]
    fn test_env_var_resolution() {
        // Test default value
        let result = ManifestResolver::resolve_env_var("${NONEXISTENT:-default_value}")
            .expect("Should resolve with default");
        assert_eq!(result, "default_value");

        // Test plain string
        let result =
            ManifestResolver::resolve_env_var("plain_string").expect("Should return plain string");
        assert_eq!(result, "plain_string");
    }
}
