//! Theme detection from HTTP headers, cookies, and browser preferences

use std::str::FromStr;

use axum::http::HeaderMap;

#[derive(Debug, Clone, PartialEq)]
pub enum Theme {
    Light,
    Dark,
    Auto, // Follows system preference
}

impl Theme {
    pub fn as_str(&self) -> &'static str {
        match self {
            Theme::Light => "light",
            Theme::Dark => "dark",
            Theme::Auto => "auto",
        }
    }
}

impl FromStr for Theme {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(match s.to_lowercase().as_str() {
            "dark" => Theme::Dark,
            "light" => Theme::Light,
            "auto" => Theme::Auto,
            _ => Theme::Auto, // Default
        })
    }
}

/// Get theme preference from cookies
pub fn get_theme_from_cookie(headers: &HeaderMap) -> Option<Theme> {
    let cookie_header = headers.get("cookie")?;
    let cookie_str = cookie_header.to_str().ok()?;

    for cookie in cookie_str.split(';') {
        let parts: Vec<&str> = cookie.trim().splitn(2, '=').collect();
        if parts.len() == 2 && parts[0] == "theme_preference" {
            let theme_str = parts[1].trim_matches('"');
            // FromStr for Theme never fails (Infallible)
            return theme_str.parse().ok();
        }
    }

    None
}

/// Get browser color scheme preference from Client Hints
pub fn get_browser_color_scheme(headers: &HeaderMap) -> Option<Theme> {
    // Check for Client Hints header (newer browsers)
    if let Some(header) = headers.get("sec-ch-prefers-color-scheme") {
        if let Ok(value) = header.to_str() {
            // FromStr for Theme never fails (Infallible)
            return value.parse().ok();
        }
    }

    None
}

/// Detect theme for SSR with fallback strategy
pub fn detect_ssr_theme(headers: &HeaderMap) -> Theme {
    // 1. Check cookie (explicit user preference)
    if let Some(cookie_theme) = get_theme_from_cookie(headers) {
        return cookie_theme;
    }

    // 2. Check Client Hints (browser preference)
    if let Some(browser_theme) = get_browser_color_scheme(headers) {
        return browser_theme;
    }

    // 3. Default to auto (let CSS handle it)
    Theme::Auto
}

/// Get CSS class for theme
pub fn get_theme_css_class(theme: &Theme) -> &'static str {
    match theme {
        Theme::Light => "theme-light",
        Theme::Dark => "theme-dark",
        Theme::Auto => "theme-auto", // CSS will handle with @media queries
    }
}

/// Generate theme-aware HTML attributes
pub fn get_theme_html_attributes(theme: &Theme) -> String {
    format!(
        r#"data-theme="{}" class="{}""#,
        theme.as_str(),
        get_theme_css_class(theme)
    )
}

/// Check if dark mode should be enabled
pub fn is_dark_mode(theme: &Theme, _headers: &HeaderMap) -> bool {
    match theme {
        Theme::Dark => true,
        Theme::Light => false,
        Theme::Auto => {
            // For auto, we can't reliably detect on server
            // Default to light mode, let client JavaScript take over
            false
        }
    }
}

/// Generate inline CSS for theme
pub fn get_theme_inline_css(theme: &Theme) -> String {
    match theme {
        Theme::Light => r#"
            :root {
                --bg-color: #ffffff;
                --text-color: #1a1a1a;
                --border-color: #e5e5e5;
            }
            "#
        .to_string(),
        Theme::Dark => r#"
            :root {
                --bg-color: #1a1a1a;
                --text-color: #ffffff;
                --border-color: #404040;
            }
            "#
        .to_string(),
        Theme::Auto => r#"
            :root {
                --bg-color: #ffffff;
                --text-color: #1a1a1a;
                --border-color: #e5e5e5;
            }
            @media (prefers-color-scheme: dark) {
                :root {
                    --bg-color: #1a1a1a;
                    --text-color: #ffffff;
                    --border-color: #404040;
                }
            }
            "#
        .to_string(),
    }
}

#[cfg(test)]
mod tests {
    use axum::http::HeaderValue;

    use super::*;

    fn create_headers_with_cookie(cookie: &str) -> HeaderMap {
        let mut headers = HeaderMap::new();
        if let Ok(value) = HeaderValue::from_str(cookie) {
            headers.insert("cookie", value);
        }
        headers
    }

    fn create_headers_with_client_hint(hint: &str) -> HeaderMap {
        let mut headers = HeaderMap::new();
        if let Ok(value) = HeaderValue::from_str(hint) {
            headers.insert("sec-ch-prefers-color-scheme", value);
        }
        headers
    }

    #[test]
    fn test_theme_from_str() {
        assert_eq!(Theme::from_str("dark"), Ok(Theme::Dark));
        assert_eq!(Theme::from_str("light"), Ok(Theme::Light));
        assert_eq!(Theme::from_str("auto"), Ok(Theme::Auto));
        assert_eq!(Theme::from_str("invalid"), Ok(Theme::Auto));
    }

    #[test]
    fn test_get_theme_from_cookie() {
        let headers = create_headers_with_cookie("theme_preference=dark; other=value");
        assert_eq!(get_theme_from_cookie(&headers), Some(Theme::Dark));

        let headers = create_headers_with_cookie("theme_preference=light");
        assert_eq!(get_theme_from_cookie(&headers), Some(Theme::Light));

        let headers = HeaderMap::new();
        assert_eq!(get_theme_from_cookie(&headers), None);
    }

    #[test]
    fn test_get_browser_color_scheme() {
        let headers = create_headers_with_client_hint("dark");
        assert_eq!(get_browser_color_scheme(&headers), Some(Theme::Dark));

        let headers = create_headers_with_client_hint("light");
        assert_eq!(get_browser_color_scheme(&headers), Some(Theme::Light));
    }

    #[test]
    fn test_detect_ssr_theme() {
        // Cookie takes priority
        let mut headers = HeaderMap::new();
        if let Ok(value) = HeaderValue::from_str("theme_preference=dark") {
            headers.insert("cookie", value);
        }
        if let Ok(value) = HeaderValue::from_str("light") {
            headers.insert("sec-ch-prefers-color-scheme", value);
        }
        assert_eq!(detect_ssr_theme(&headers), Theme::Dark);

        // Falls back to client hint
        let headers = create_headers_with_client_hint("dark");
        assert_eq!(detect_ssr_theme(&headers), Theme::Dark);

        // Falls back to auto
        let headers = HeaderMap::new();
        assert_eq!(detect_ssr_theme(&headers), Theme::Auto);
    }

    #[test]
    fn test_get_theme_css_class() {
        assert_eq!(get_theme_css_class(&Theme::Dark), "theme-dark");
        assert_eq!(get_theme_css_class(&Theme::Light), "theme-light");
        assert_eq!(get_theme_css_class(&Theme::Auto), "theme-auto");
    }

    #[test]
    fn test_is_dark_mode() {
        let headers = HeaderMap::new();
        assert!(is_dark_mode(&Theme::Dark, &headers));
        assert!(!is_dark_mode(&Theme::Light, &headers));
        assert!(!is_dark_mode(&Theme::Auto, &headers)); // Server default
    }
}
