//! Orchestrator combining every per-page tag group into one `<head>` slice.

use crate::types::PageSeo;
use crate::{canonical, og, tags, twitter};

/// Render the full per-page `<head>` slice: title, description, keywords,
/// robots, Open Graph, Twitter cards, canonical and hreflang, plus JSON-LD
/// blocks when the `jsonld` feature is enabled.
///
/// Does **not** include `<head>` open/close tags, `<meta charset>`,
/// `<meta viewport>`, stylesheets, or scripts — those are the shell's
/// responsibility because they are render-target specific (e.g. WASM
/// hydration bootstrap vs htmx runtime).
///
/// # Errors
///
/// Returns [`SeoError::JsonSerialization`](crate::SeoError) only when the
/// `jsonld` feature is enabled and a block fails to serialize.
pub fn render_head_tags(seo: &PageSeo) -> crate::Result<String> {
    let mut out = String::new();
    out.push_str(&tags::render_basic_tags(seo));
    out.push_str(&canonical::render_canonical_tags(seo));
    out.push_str(&og::render_og_tags(seo));
    out.push_str(&twitter::render_twitter_tags(seo));
    #[cfg(feature = "jsonld")]
    if !seo.jsonld.is_empty() {
        out.push_str(&crate::jsonld::render_jsonld_blocks(&seo.jsonld)?);
    }
    Ok(out)
}

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

    #[test]
    fn includes_every_tag_group() {
        let mut seo = PageSeo::new("Home", "https://example.com/", "en");
        seo.description = Some("welcome page".into());
        let html = render_head_tags(&seo).expect("render");
        assert!(html.contains("<title>Home</title>"));
        assert!(html.contains(r#"name="description""#));
        assert!(html.contains(r#"rel="canonical""#));
        assert!(html.contains(r#"property="og:type""#));
        assert!(html.contains(r#"name="twitter:card""#));
    }

    #[cfg(feature = "jsonld")]
    #[test]
    fn appends_jsonld_blocks_when_present() {
        use serde_json::json;

        use crate::{JsonLdBlock, JsonLdSchema};
        let mut seo = PageSeo::new("Post", "https://example.com/blog/x", "en");
        seo.jsonld.push(JsonLdBlock::new(
            JsonLdSchema::BlogPosting,
            json!({"headline": "Post"}),
        ));
        let html = render_head_tags(&seo).expect("render");
        assert!(html.contains(r#"type="application/ld+json""#));
        assert!(html.contains(r#""@type":"BlogPosting""#));
    }
}
