//! HTML Generation Module for PostViewer
//!
//! Handles all HTML content processing, extraction, and DOM manipulation
//! for both SSR and WASM environments.

use std::collections::HashMap;

/// HTML content processor for extracting and cleaning content
pub struct HtmlProcessor;

impl HtmlProcessor {
    /// Extract article content from HTML, preferring <main> over <body>, excluding back-link
    #[cfg(target_arch = "wasm32")]
    pub fn extract_article_content(html: &str) -> String {
        if let Some(window) = web_sys::window() {
            if let Some(document) = window.document() {
                // Create a new div for parsing
                if let Ok(temp_element) = document.create_element("div") {
                    temp_element.set_inner_html(html);
                    let remove_h1 = false;

                    // Try to find <main> first
                    if let Ok(Some(main_element)) = temp_element.query_selector("main") {
                        return Self::clean_element_content(&main_element, remove_h1);
                    }

                    // Fallback to <body> if <main> doesn't exist
                    if let Ok(Some(body_element)) = temp_element.query_selector("body") {
                        return Self::clean_element_content(&body_element, remove_h1);
                    }
                }
            }
        }

        // Fallback: return original content if DOM parsing fails
        html.to_string()
    }

    /// Clean element content by removing unwanted elements
    #[cfg(target_arch = "wasm32")]
    fn clean_element_content(element: &web_sys::Element, remove_h1: bool) -> String {
        // Remove back-link if it exists
        if let Ok(Some(back_link)) = element.query_selector(".back-link") {
            let _ = back_link.remove();
        }

        // Remove the first h1 to prevent title duplication if requested
        if remove_h1 {
            if let Ok(Some(h1_element)) = element.query_selector("h1") {
                let _ = h1_element.remove();
            }
        }

        element.inner_html()
    }

    /// Create loading placeholder HTML
    pub fn create_loading_placeholder() -> String {
        "<div class='text-center py-8 text-gray-600'>Loading content...</div>".to_string()
    }

    /// Create error content HTML with proper styling
    pub fn create_error_content(title: &str, message: &str, error_detail: Option<&str>) -> String {
        let error_section = if let Some(detail) = error_detail {
            format!(
                "<p class='text-gray-600 mb-4'>Error: {}</p>",
                html_escape::encode_text(detail)
            )
        } else {
            String::new()
        };

        format!(
            r#"<div class='text-center py-8'>
                <div class='text-red-500 mb-4'>
                    <svg class='w-16 h-16 mx-auto mb-4' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
                        <path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z'></path>
                    </svg>
                </div>
                <h3 class='text-lg font-semibold text-red-600 mb-2'>{}</h3>
                <p class='text-gray-600 mb-4'>{}</p>
                {}
                <p class='text-sm text-gray-500'>Please try refreshing the page or check the URL</p>
                <button onclick='window.location.reload()' class='mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors'>
                    Refresh Page
                </button>
            </div>"#,
            html_escape::encode_text(title),
            html_escape::encode_text(message),
            error_section
        )
    }

    /// Wrap content in prose styling
    pub fn wrap_in_prose(content: &str) -> String {
        format!(
            "<div class='prose prose-gray dark:prose-invert max-w-none bg-transparent'>{}</div>",
            content
        )
    }
}

/// Content builder for creating structured content maps
pub struct ContentBuilder {
    content: HashMap<String, String>,
}

impl ContentBuilder {
    pub fn new() -> Self {
        Self {
            content: HashMap::new(),
        }
    }

    /// Add a content entry
    pub fn add(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.content.insert(key.into(), value.into());
        self
    }

    /// Add title content
    pub fn title(self, title: impl Into<String>) -> Self {
        self.add("post-title", title)
    }

    /// Add subtitle content
    pub fn subtitle(self, subtitle: impl Into<String>) -> Self {
        self.add("post-subtitle", subtitle)
    }

    /// Add main content
    pub fn content(self, content: impl Into<String>) -> Self {
        self.add("post-content", content)
    }

    /// Add loading placeholder content
    pub fn loading_content(self) -> Self {
        self.content(HtmlProcessor::create_loading_placeholder())
    }

    /// Add error content
    pub fn error_content(self, title: &str, message: &str, error_detail: Option<&str>) -> Self {
        self.content(HtmlProcessor::create_error_content(
            title,
            message,
            error_detail,
        ))
    }

    /// Build the final content map
    pub fn build(self) -> HashMap<String, String> {
        self.content
    }

    /// Merge with base content
    pub fn merge_with_base(mut self, base_content: HashMap<String, String>) -> Self {
        for (key, value) in base_content {
            self.content.entry(key).or_insert(value);
        }
        self
    }
}

impl Default for ContentBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// DOM manipulation utilities for WASM contexts
#[cfg(target_arch = "wasm32")]
pub struct DomUtils;

#[cfg(target_arch = "wasm32")]
impl DomUtils {
    /// Update content element with new HTML
    pub fn update_content_element(html_content: &str) -> bool {
        if let Some(window) = web_sys::window() {
            if let Some(document) = window.document() {
                if let Ok(Some(content_element)) =
                    document.query_selector(".ds-text-secondary.space-y-4")
                {
                    let processed_content = HtmlProcessor::wrap_in_prose(html_content);
                    content_element.set_inner_html(&processed_content);
                    return true;
                }
            }
        }
        false
    }

    /// Show/hide title section
    pub fn toggle_title_section(visible: bool) -> bool {
        if let Some(window) = web_sys::window() {
            if let Some(document) = window.document() {
                if let Some(title_section) = document.get_element_by_id("title-section") {
                    let class_list = title_section.class_list();
                    if visible {
                        let _ = class_list.remove_1("hidden");
                    } else {
                        let _ = class_list.add_1("hidden");
                    }
                    return true;
                }
            }
        }
        false
    }

    /// Log message to browser console
    pub fn console_log(message: &str) {
        web_sys::console::log_1(&message.into());
    }

    /// Log error to browser console
    pub fn console_error(message: &str) {
        web_sys::console::error_1(&message.into());
    }

    /// Log warning to browser console
    pub fn console_warn(message: &str) {
        web_sys::console::warn_1(&message.into());
    }
}

/// Async content updater for WASM environments
#[cfg(target_arch = "wasm32")]
pub struct AsyncContentUpdater;

#[cfg(target_arch = "wasm32")]
impl AsyncContentUpdater {
    /// Update content asynchronously after fetching HTML
    pub async fn update_with_html_fetch(html_path: String) {
        use crate::post_viewer::content_loader::PostViewerContentLoader;

        let loader = PostViewerContentLoader::new();

        DomUtils::console_log(&format!("🌐 Fetching HTML from: {}", html_path));

        match loader.load_html_content(&html_path).await {
            Ok(html_content) => {
                DomUtils::console_log(&format!(
                    "✅ Successfully loaded {} chars",
                    html_content.len()
                ));

                // Extract only the article content
                let clean_content = HtmlProcessor::extract_article_content(&html_content);

                // Update the content element
                if DomUtils::update_content_element(&clean_content) {
                    // Hide the title section once content is loaded
                    DomUtils::toggle_title_section(false);
                } else {
                    DomUtils::console_error("Failed to find content element for update");
                }
            }
            Err(error) => {
                DomUtils::console_error(&format!("❌ HTML fetch error: {}", error));
            }
        }
    }
}

/// Predefined content templates
pub struct ContentTemplates;

impl ContentTemplates {
    /// Create content not found template
    pub fn content_not_found(base_content: HashMap<String, String>) -> HashMap<String, String> {
        ContentBuilder::new()
            .title("Content Not Found")
            .subtitle("Content not available")
            .content("Content item not found")
            .merge_with_base(base_content)
            .build()
    }

    /// Create index error template
    pub fn index_error(base_content: HashMap<String, String>) -> HashMap<String, String> {
        ContentBuilder::new()
            .title("Content Index Error")
            .subtitle("Failed to load content index")
            .content("Failed to load content index")
            .merge_with_base(base_content)
            .build()
    }

    /// Create no slug template
    pub fn no_slug(base_content: HashMap<String, String>) -> HashMap<String, String> {
        ContentBuilder::new()
            .title("No Content Slug")
            .subtitle("No slug provided in URL")
            .content("No slug provided")
            .merge_with_base(base_content)
            .build()
    }

    /// Create loading template with item data
    pub fn loading_with_item(
        base_content: HashMap<String, String>,
        title: String,
        subtitle: String,
    ) -> HashMap<String, String> {
        ContentBuilder::new()
            .title(title)
            .subtitle(subtitle)
            .loading_content()
            .merge_with_base(base_content)
            .build()
    }
}

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

    #[test]
    fn test_content_builder() {
        let content = ContentBuilder::new()
            .title("Test Title")
            .subtitle("Test Subtitle")
            .content("Test Content")
            .build();

        assert_eq!(content.get("post-title"), Some(&"Test Title".to_string()));
        assert_eq!(
            content.get("post-subtitle"),
            Some(&"Test Subtitle".to_string())
        );
        assert_eq!(
            content.get("post-content"),
            Some(&"Test Content".to_string())
        );
    }

    #[test]
    fn test_html_processor_loading_placeholder() {
        let placeholder = HtmlProcessor::create_loading_placeholder();
        assert!(placeholder.contains("Loading content..."));
        assert!(placeholder.contains("text-center"));
    }

    #[test]
    fn test_content_templates() {
        let base = HashMap::new();
        let not_found = ContentTemplates::content_not_found(base);

        assert_eq!(
            not_found.get("post-title"),
            Some(&"Content Not Found".to_string())
        );
        assert_eq!(
            not_found.get("post-subtitle"),
            Some(&"Content not available".to_string())
        );
    }
}
