//! Route dispatcher for HTMX page rendering.
//!
//! Content post routes load metadata from the index and rendered HTML from
//! `load_content_by_slug`. Static project pages are handled by `website_pages_htmx`.
//! Unknown routes fall back to 404. No Leptos in this module.

use rustelo_core_lib::{
    load_content_by_slug, load_content_index, process_markdown_file, rewrite_content_image_paths,
};
use rustelo_pages_htmx::{render_not_found_page, render_post_page};

/// Render the page body for `path` in `lang`. Never panics.
pub fn dispatch(path: &str, lang: &str) -> String {
    let env = crate::htmx_env::htmx_env();

    if let Some(html) = try_content_post(path, lang) {
        return html;
    }

    dispatch_static(path, lang)
}

/// Render a static project/product page or the 404 page — no content-post lookup.
///
/// Used by the shell after it has already determined the path is not a content
/// post (so the post lookup isn't repeated).
pub fn dispatch_static(path: &str, lang: &str) -> String {
    let env = crate::htmx_env::htmx_env();
    if let Some(html) = website_pages_htmx::dispatch(&env, path, lang) {
        return html;
    }
    render_not_found_page(&env, path, lang)
}

/// Render a content post if `path` resolves to an existing content item.
///
/// Authoritative post check: returns `Some(html)` only when an item with the
/// path's slug exists in some language index. The shell calls this before
/// falling back to the content grid, so a real post URL always loads the post.
pub fn try_content_post(path: &str, lang: &str) -> Option<String> {
    // Config-driven kind resolution: the route table (site/config/routes.ncl,
    // registered at startup) maps /adr/{slug}, /proyectos/{slug}, … to their
    // content kind. No kinds are hardcoded here — a new kind needs only its
    // content.ncl declaration + routes.ncl entry. A non-content path resolves to
    // a kind with no index and falls through below.
    let kind = rustelo_core_lib::content_resolver::resolve_content_type_from_route(path, lang).ok()?;
    let slug = path
        .trim_end_matches('/')
        .rsplit('/')
        .next()
        .filter(|s| !s.is_empty())?;
    let env = crate::htmx_env::htmx_env();

    let default_lang = rustelo_core_lib::config::get_default_language();
    let mut candidate_langs: Vec<&str> = vec![lang, default_lang];
    for l in rustelo_core_lib::config::get_supported_languages() {
        candidate_langs.push(l.as_str());
    }
    candidate_langs.dedup();

    // Step 1 — find the canonical item id by matching the URL slug against any
    // language index. The URL may carry the EN slug even when the user has switched
    // to ES, so we search by slug / localized_slug / id across all candidates.
    let canonical_id: String = candidate_langs.iter().find_map(|&cand| {
        let idx = load_content_index(&kind, cand).ok()?;
        idx.items.iter().find(|i| {
            i.published
                && (i.slug == slug || i.localized_slug.as_deref() == Some(slug) || i.id == slug)
        }).map(|i| i.id.clone())
    })?;

    // Step 2 — load the preferred-language version of the item by canonical id.
    // Falls back to whichever language has it when the preferred language does not.
    let (item, effective_lang) = candidate_langs.iter().find_map(|&cand| {
        let idx = load_content_index(&kind, cand).ok()?;
        let it = idx.items.iter().find(|i| i.published && i.id == canonical_id)?.clone();
        Some((it, cand))
    })?;

    // Use item.slug (language-specific) for content loading, not the URL slug.
    let raw_markdown = load_content_by_slug(&item.slug, &kind, effective_lang).unwrap_or_else(|e| {
        tracing::warn!(item_slug = item.slug, kind = kind.as_str(), effective_lang, error = %e, "content body load failed");
        String::new()
    });

    let body_html = if raw_markdown.is_empty() {
        String::new()
    } else {
        match process_markdown_file(&raw_markdown) {
            Ok((_meta, html)) => rewrite_content_image_paths(&html, &kind, effective_lang, &item.slug),
            Err(e) => {
                tracing::warn!(slug, kind = kind.as_str(), lang, error = %e, "markdown render failed");
                raw_markdown
            }
        }
    };

    let post_html = render_post_page(&env, &item, &body_html, effective_lang);
    // Graph is keyed by canonical id (not language-specific slug).
    let sidebar_html = website_pages::content_graph::render_sidebar(&canonical_id, effective_lang);

    if sidebar_html.is_empty() {
        return Some(post_html);
    }

    Some(format!(
        r#"<div class="ds-container py-ds-6"><div class="lg:grid lg:grid-cols-[1fr_280px] lg:gap-8 space-y-6 lg:space-y-0"><div class="min-w-0">{post_html}</div>{sidebar_html}</div></div>"#,
    ))
}
