//! Process-wide Minijinja `Environment` for HTMX page rendering.
//!
//! Uses `RwLock<Option<Arc<Environment>>>` so the environment can be reset
//! at runtime (e.g. after template hot-deploy) without restarting the server.
//! Call `reset_htmx_env()` to force re-initialisation on the next request.

use std::sync::{Arc, RwLock};

use minijinja::Environment;

static ENV: RwLock<Option<Arc<Environment<'static>>>> = RwLock::new(None);

/// Return the current Minijinja environment, initialising it lazily on first call.
///
/// Returns an `Arc` clone — callers hold the env for the duration of the request
/// without blocking writers. Dereference with `&*env` where `&Environment` is needed.
pub fn htmx_env() -> Arc<Environment<'static>> {
    // Fast path: already initialised
    if let Some(env) = ENV.read().expect("htmx env lock poisoned").as_ref() {
        return Arc::clone(env);
    }
    // Slow path: acquire write lock, double-check, then build
    let mut guard = ENV.write().expect("htmx env lock poisoned");
    if let Some(env) = guard.as_ref() {
        return Arc::clone(env);
    }
    let env = Arc::new(build_env());
    *guard = Some(Arc::clone(&env));
    env
}

/// Force re-initialisation on the next call to `htmx_env()`.
///
/// Use after changing `HTMX_TEMPLATE_PATH` or when templates are updated in
/// production without a server restart. In-flight requests that already hold
/// an `Arc` to the old environment finish normally — only new requests get
/// the fresh environment.
pub fn reset_htmx_env() {
    *ENV.write().expect("htmx env lock poisoned") = None;
    tracing::info!("Minijinja environment reset — will reinitialise on next request");
}

fn build_env() -> Environment<'static> {
    // Project templates override framework defaults: check project path first,
    // fall through to framework path if not found.
    let project_path = std::env::var("HTMX_TEMPLATE_PATH")
        .unwrap_or_else(|_| website_pages_htmx::TEMPLATES_DIR.to_string());
    let framework_path = rustelo_pages_htmx::TEMPLATES_DIR.to_string();

    tracing::info!(
        project = %project_path,
        project_exists = %std::path::Path::new(&project_path).exists(),
        framework = %framework_path,
        framework_exists = %std::path::Path::new(&framework_path).exists(),
        "initialising Minijinja environment (cascade: project → framework)"
    );

    let mut env = Environment::new();
    env.set_loader(move |name: &str| -> Result<Option<String>, minijinja::Error> {
        // 1. Project templates (highest priority)
        let project_file = std::path::Path::new(&project_path).join(name);
        if project_file.exists() {
            return std::fs::read_to_string(&project_file)
                .map(Some)
                .map_err(|e| minijinja::Error::new(
                    minijinja::ErrorKind::InvalidOperation,
                    format!("failed to read template {name}: {e}"),
                ));
        }
        // 2. Framework templates (fallback)
        let framework_file = std::path::Path::new(&framework_path).join(name);
        if framework_file.exists() {
            return std::fs::read_to_string(&framework_file)
                .map(Some)
                .map_err(|e| minijinja::Error::new(
                    minijinja::ErrorKind::InvalidOperation,
                    format!("failed to read template {name}: {e}"),
                ));
        }
        Ok(None)
    });
    rustelo_pages_htmx::setup_environment(&mut env);
    env
}
