//! Resource registration and initialization
//!
//! This module handles the registration of all website resources (menus, footers,
//! themes, and FTL translations) with the Rustelo core library's registration system.
//!
//! Resources come from:
//! - config/menus/*.toml
//! - config/footers/*.toml
//! - config/themes/*.toml
//! - content/i18n/*.ftl
//!
//! The generated ResourceContributor is created at build-time by build.rs

// Include the generated ResourceContributor implementation
include!(concat!(env!("OUT_DIR"), "/resource_contributor.rs"));

/// Register all website resources with the Rustelo core library
///
/// This function should be called at server startup, before any resources are accessed.
///
/// # Errors
///
/// Returns an error if registration with the core library fails
pub fn register_resources() -> Result<(), Box<dyn std::error::Error>> {
    use rustelo_core_lib::register_contributor;

    // Register the website's resources with the core library
    register_contributor(&WebsiteResourceContributor)?;

    eprintln!("✅ Website resources registered successfully");
    Ok(())
}

/// Register content kinds from compile-time constants generated from site/config/index.ncl.
///
/// `crate::config_constants::CONTENT_TYPES` is produced by `server/build.rs` at compile time.
/// No file I/O at runtime — the TOML fallback (`content-kinds.toml`) is no longer needed.
fn register_content_kinds() -> Result<(), Box<dyn std::error::Error>> {
    use rustelo_core_lib::{ContentConfig, ContentKindRegistry};

    // Resolve content base directory: SITE_CONTENT_PATH env var takes priority,
    // otherwise walk ancestors until site/content/ is found.
    let content_base = if let Ok(p) = std::env::var("SITE_CONTENT_PATH") {
        std::path::PathBuf::from(p)
    } else {
        let cwd = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
        std::iter::once(cwd.as_path())
            .chain(cwd.ancestors())
            .find(|p| p.join("site").join("content").exists())
            .unwrap_or(&cwd)
            .join("site")
            .join("content")
    };

    let mut registry = ContentKindRegistry::new();
    for &(name, directory) in crate::config_constants::CONTENT_TYPES {
        let locales_path = content_base.join(directory);
        registry.register(
            name.to_string(),
            ContentConfig {
                name: name.to_string(),
                directory: directory.to_string(),
                enabled: true,
                is_default: None,
                features: Some(rustelo_core_lib::ContentFeatures::default()),
                locales_path,
            },
        );
    }

    rustelo_core_lib::register_content_kind_registry(registry);
    Ok(())
}

/// Inject NCL-derived database settings into the process environment.
///
/// These values come from `site/config.ncl [database]`, embedded at compile time.
/// Priority is: shell env var > .env DATABASE_URL > NCL defaults.
///
/// Must be called before `Config::load()` (i.e. before `rustelo_server::run::run_server()`).
fn inject_ncl_database_env() {
    use crate::config_constants::{
        NCL_DATABASE_CONNECT_TIMEOUT, NCL_DATABASE_CREATE_IF_MISSING, NCL_DATABASE_IDLE_TIMEOUT,
        NCL_DATABASE_MAX_CONNECTIONS, NCL_DATABASE_MAX_LIFETIME, NCL_DATABASE_MIN_CONNECTIONS,
        NCL_DATABASE_URL,
    };

    // DATABASE_URL: .env wins (dotenv not yet run here, so only shell-exported vars are present).
    // Config::load() calls dotenv() which sets DATABASE_URL from .env (if not already in shell).
    // NCL_DATABASE_URL is a lower-priority fallback checked in Config::apply_env_overrides().
    if std::env::var("DATABASE_URL").is_err() {
        std::env::set_var("NCL_DATABASE_URL", NCL_DATABASE_URL);
    }
    std::env::set_var(
        "NCL_DATABASE_CREATE_IF_MISSING",
        if NCL_DATABASE_CREATE_IF_MISSING {
            "true"
        } else {
            "false"
        },
    );
    std::env::set_var(
        "NCL_DATABASE_MAX_CONNECTIONS",
        NCL_DATABASE_MAX_CONNECTIONS.to_string(),
    );
    std::env::set_var(
        "NCL_DATABASE_MIN_CONNECTIONS",
        NCL_DATABASE_MIN_CONNECTIONS.to_string(),
    );
    std::env::set_var(
        "NCL_DATABASE_CONNECT_TIMEOUT",
        NCL_DATABASE_CONNECT_TIMEOUT.to_string(),
    );
    std::env::set_var(
        "NCL_DATABASE_IDLE_TIMEOUT",
        NCL_DATABASE_IDLE_TIMEOUT.to_string(),
    );
    std::env::set_var(
        "NCL_DATABASE_MAX_LIFETIME",
        NCL_DATABASE_MAX_LIFETIME.to_string(),
    );
}

/// Initialize all resources (registration + config loading)
///
/// This is the main initialization function that should be called at server startup.
/// It performs:
/// 1. Registration of the WebsiteResourceContributor with the core library
/// 2. Loading of configuration files into the resource registries
/// 3. Registration of content kinds from `site/content/content-kinds.toml`
///
/// # Errors
///
/// Returns an error if either registration or config loading fails
pub fn initialize() -> Result<(), Box<dyn std::error::Error>> {
    eprintln!("🔄 Initializing resources...");

    // Inject NCL database config as env vars so Config::load() picks them up.
    inject_ncl_database_env();

    // V2 renderer is profile-aware: receives RenderingProfile + htmx_extensions
    // from the framework dispatcher, so a single flip in rendering.ncl switches
    // between Leptos-hydration and HTMX-SSR without any code change.
    rustelo_server::run::register_shell_renderer_v2(crate::run::render_shell_v2);
    // Fragment renderer: returns <main> only for non-boosted HX-Request responses.
    rustelo_server::run::register_fragment_renderer(crate::run::render_fragment);
    // Fragment environment: all framework HTMX fragment renderers use this to
    // render via Minijinja templates instead of inline format strings.
    rustelo_pages_htmx::register_fragment_env(crate::htmx_env::htmx_env());
    // Content grid renderer for /api/htmx/content/{kind} (F-08).
    rustelo_server::api::register_content_grid_renderer(crate::htmx_grid::render_content_grid);

    // Apply the rendering profile declared in site/config/rendering.ncl. When
    // the file is absent or omits the [rendering] table the framework keeps
    // its default (LeptosHydration), so this is back-compat by construction.
    install_rendering_profile();

    // Register resources
    register_resources()?;

    // Load resources from configuration files
    rustelo_core_lib::load_resources_from_config()?;

    // Register content kinds so SimpleContentGrid finds the registry
    register_content_kinds()?;

    eprintln!("✅ Resources initialized successfully");
    Ok(())
}

/// Load the `[rendering]` table from `site/config/rendering.ncl` (when present)
/// and apply it to the framework-level rendering profile registry.
///
/// Resolution order:
///   1. `RENDERING_CONFIG_PATH` env var (absolute or relative).
///   2. `NCL_CONFIG_PATH`'s sibling `rendering.ncl`.
///   3. `site/config/rendering.ncl` resolved against CWD.
///
/// Failure modes are intentionally non-fatal: missing file, parse error, or
/// absent `[rendering]` table all log a single line and keep the framework
/// default (`RenderingProfile::LeptosHydration`). This keeps the migration to
/// htmx-ssr opt-in even when the loader is wired.
fn install_rendering_profile() {
    let Some(ncl_path) = locate_rendering_ncl() else {
        eprintln!(
            "🎨 Rendering profile: no rendering.ncl located → default LeptosHydration"
        );
        return;
    };
    match rustelo_server::run::install_workspace_rendering_from_file(&ncl_path) {
        Ok(Some(cfg)) => {
            eprintln!(
                "🎨 Rendering profile: default={} extensions={:?}",
                cfg.default_profile, cfg.htmx_extensions
            );
        }
        Ok(None) => {
            eprintln!(
                "🎨 Rendering profile: rendering.ncl has no [rendering] table → default LeptosHydration"
            );
        }
        Err(e) => {
            eprintln!(
                "⚠️  Rendering profile: load failed ({e}); using default LeptosHydration"
            );
        }
    }
}

fn locate_rendering_ncl() -> Option<std::path::PathBuf> {
    if let Ok(explicit) = std::env::var("RENDERING_CONFIG_PATH") {
        let p = std::path::PathBuf::from(explicit);
        if p.exists() {
            return Some(p);
        }
    }
    if let Ok(base) = std::env::var("NCL_CONFIG_PATH") {
        let p = std::path::PathBuf::from(&base);
        let candidate = if p.is_dir() {
            p.join("rendering.ncl")
        } else if let Some(parent) = p.parent() {
            parent.join("rendering.ncl")
        } else {
            p.clone()
        };
        if candidate.exists() {
            return Some(candidate);
        }
    }
    std::env::current_dir()
        .ok()
        .map(|c| c.join("site/config/rendering.ncl"))
        .filter(|p| p.exists())
}

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

    #[test]
    fn test_resource_contributor_exists() {
        let _contributor = WebsiteResourceContributor;
        // Just verify it can be instantiated
    }

    #[test]
    fn test_themes_are_loaded() {
        let contributor = WebsiteResourceContributor;
        let themes = contributor.contribute_themes();
        // Verify at least some themes were loaded (should have default, dark, etc.)
        assert!(!themes.is_empty(), "At least one theme should be loaded");
    }

    #[test]
    fn test_menus_are_optional() {
        let contributor = WebsiteResourceContributor;
        let menus = contributor.contribute_menus();
        // Menus are optional, so we just check the method works
        assert!(menus.is_empty() || !menus.is_empty());
    }
}
