//! Builds the [`PageSeo`] descriptor consumed by both shells.
//!
//! Resolution order, in priority:
//! 1. Generated route component (title/description/keywords from FTL keys).
//! 2. Site defaults from `site/config/site.ncl` (site name, default locale).
//! 3. Hreflang alternates derived from configured languages.

use rustelo_core_lib::config::get_supported_languages;
use rustelo_seo::{
    join_url, HreflangAlt, OgImage, OgType, OpenGraph, PageSeo, RobotsDirective, TwitterCard,
    TwitterCardKind,
};
use rustelo_web::rustelo_core_lib::i18n::{PageTranslator, SsrTranslator};

/// Build a [`PageSeo`] for the requested path + language.
///
/// `base_url` is the fully-qualified site origin (e.g. `https://example.com`)
/// used for canonical and hreflang URLs.
pub fn build_page_seo(path: &str, language: &str, base_url: &str) -> PageSeo {
    let component = {
        let base = crate::resolve_component_for_path(path);
        let with_page = crate::generated::RouteComponent::from_str(&format!("{}Page", base));
        if with_page != crate::generated::RouteComponent::NotFound || base == "NotFound" {
            with_page
        } else {
            crate::generated::RouteComponent::from_str(base)
        }
    };

    let translator = SsrTranslator::new(language.to_string());
    let raw_title = component.title_key();
    let translated_title = translator.t(raw_title);
    let title = if translated_title.is_empty() {
        raw_title.to_string()
    } else {
        translated_title
    };
    let raw_desc = component.description_key();
    let translated_desc = translator.t(raw_desc);
    let description = if translated_desc.is_empty() || translated_desc == raw_desc {
        None
    } else {
        Some(translated_desc)
    };
    let raw_keywords = component.keywords_key();
    let translated_keywords = translator.t(raw_keywords);
    let keywords = if translated_keywords.is_empty() || translated_keywords == raw_keywords {
        Vec::new()
    } else {
        translated_keywords
            .split(',')
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect::<Vec<_>>()
    };

    let site_name = std::env::var("SITE_NAME").unwrap_or_else(|_| "Rustelo".to_string());
    let title_with_site = if title.contains(&site_name) || title.is_empty() {
        title.clone()
    } else {
        format!("{site_name} — {title}")
    };

    let canonical_path = canonical_path_for(path, language);
    let canonical = join_url(base_url, &canonical_path);

    let hreflang = build_hreflang(path, base_url);

    let og = OpenGraph {
        title: title_with_site.clone(),
        description: description.clone(),
        og_type: OgType::Website,
        url: canonical.clone(),
        image: Some(OgImage::new(format!("{}/images/og/default.png", base_url.trim_end_matches('/')))),
        site_name: Some(site_name.clone()),
        locale: Some(locale_for(language)),
    };
    let twitter = TwitterCard {
        card: TwitterCardKind::SummaryLargeImage,
        title: title_with_site.clone(),
        description: description.clone(),
        image: og.image.as_ref().map(|i| i.url.clone()),
        image_alt: None,
        site: None,
        creator: None,
    };

    PageSeo {
        title: title_with_site,
        description,
        keywords,
        language: language.to_string(),
        canonical,
        hreflang,
        og,
        twitter,
        robots: RobotsDirective::default(),
        jsonld: Vec::new(),
    }
}

fn canonical_path_for(path: &str, language: &str) -> String {
    if language == "en" {
        path.to_string()
    } else {
        let trimmed = path.trim_start_matches('/');
        if trimmed.starts_with(&format!("{language}/")) || trimmed == language {
            path.to_string()
        } else {
            format!("/{language}{path}")
        }
    }
}

fn build_hreflang(path: &str, base_url: &str) -> Vec<HreflangAlt> {
    let langs = get_supported_languages();
    let mut out: Vec<HreflangAlt> = Vec::new();
    for lang in langs.iter() {
        let p = canonical_path_for(path, lang);
        out.push(HreflangAlt {
            lang: lang.to_string(),
            href: join_url(base_url, &p),
        });
    }
    out.push(HreflangAlt {
        lang: "x-default".into(),
        href: join_url(base_url, path),
    });
    out
}

fn locale_for(language: &str) -> String {
    match language {
        "en" => "en_US".into(),
        "es" => "es_ES".into(),
        other => other.replace('-', "_"),
    }
}

/// Returns the configured public base URL.
///
/// Reads `SITE_BASE_URL`; falls back to `http://127.0.0.1:3030` for local dev
/// so canonical links don't end up empty in development.
pub fn site_base_url() -> String {
    std::env::var("SITE_BASE_URL").unwrap_or_else(|_| "http://127.0.0.1:3030".to_string())
}

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

    #[test]
    fn canonical_path_passthrough_for_default_language() {
        assert_eq!(canonical_path_for("/about", "en"), "/about");
    }

    #[test]
    fn canonical_path_prefixes_non_default_language() {
        assert_eq!(canonical_path_for("/about", "es"), "/es/about");
    }

    #[test]
    fn canonical_path_no_double_prefix() {
        assert_eq!(canonical_path_for("/es/about", "es"), "/es/about");
    }

    #[test]
    fn locale_mapping() {
        assert_eq!(locale_for("en"), "en_US");
        assert_eq!(locale_for("es"), "es_ES");
        assert_eq!(locale_for("pt-BR"), "pt_BR");
    }
}
