use async_trait::async_trait;
use axum::{Extension, Router as AxumRouter};
use fluent_templates::{ArcLoader, FluentLoader};
use loco_rs::{
    app::{AppContext, Initializer},
    controller::views::{engines, ViewEngine},
    Error, Result,
};
use std::collections::HashMap;
use tracing::info;

const I18N_DIR: &str = "assets/i18n";
// Kept outside `I18N_DIR`: fluent-templates >=0.13 scans top-level *.ftl files
// in that dir as locales, and "shared" parses as a langid, so a shared.ftl
// living there would be loaded twice and fail with a duplicate-resource error.
const I18N_SHARED: &str = "assets/i18n_shared/shared.ftl";
#[allow(clippy::module_name_repetitions)]
pub struct ViewEngineInitializer;

#[async_trait]
impl Initializer for ViewEngineInitializer {
    fn name(&self) -> String {
        "view-engine".to_string()
    }

    async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result<AxumRouter> {
        // Load locales only if present; `t` is registered conditionally below so
        // the single post-process closure covers both cases.
        let locales = if std::path::Path::new(I18N_DIR).exists() {
            let arc = std::sync::Arc::new(
                ArcLoader::builder(&I18N_DIR, unic_langid::langid!("sk"))
                    .shared_resources(Some(&[I18N_SHARED.into()]))
                    .customize(|bundle| bundle.set_use_isolating(false))
                    .build()
                    .map_err(|e| Error::string(&e.to_string()))?,
            );
            info!("locales loaded");
            Some(arc)
        } else {
            None
        };

        let tera_engine = engines::TeraView::build()?.post_process(move |tera| {
            if let Some(arc) = &locales {
                tera.register_function("t", FluentLoader::new(arc.clone()));
            }
            // `csrf_token()`: the in-flight request's CSRF token (bound by
            // `shared::csrf::protect`), rendered into `<body hx-headers>` and
            // `ui::csrf_field()`. Inlined so its `tera::Error` return is inferred
            // from `register_function` — we never name a `tera` type, keeping it
            // off our direct deps and pinned to loco's.
            tera.register_function("csrf_token", |_args: &HashMap<String, serde_json::Value>| {
                Ok(serde_json::Value::String(
                    crate::shared::csrf::current_token().unwrap_or_default(),
                ))
            });
            // `currencies()`: the EUR base plus enabled alternative currencies
            // (from the process-wide snapshot), used by the global chrome — the
            // settings-menu switcher and the navbar exchange-rate display.
            tera.register_function("currencies", |_args: &HashMap<String, serde_json::Value>| {
                Ok(crate::shared::currency::selectable_json())
            });
            Ok(())
        })?;

        Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
    }
}
