use std::collections::HashMap;

use anyhow::Result;
use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag, TagEnd};
use rustelo_core_lib::fluent::{ContentFormat, PageContent};
// Removed syntect imports - now using client-side highlight.js

pub struct ContentRenderer {
    enable_tables: bool,
    enable_strikethrough: bool,
    enable_tasklists: bool,
    enable_footnotes: bool,
    enable_smart_punctuation: bool,
    custom_css_classes: HashMap<String, String>,
}

impl ContentRenderer {
    pub fn new() -> Self {
        Self {
            enable_tables: true,
            enable_strikethrough: true,
            enable_tasklists: true,
            enable_footnotes: true,
            enable_smart_punctuation: true,
            custom_css_classes: HashMap::new(),
        }
    }

    /// Create a lightweight ContentRenderer for testing
    #[cfg(test)]
    pub fn new_for_testing() -> Self {
        Self::new()
    }

    // Removed: with_theme and with_syntax_highlighting (now using client-side
    // highlight.js)

    #[allow(dead_code)]
    pub fn with_tables(mut self, enable: bool) -> Self {
        self.enable_tables = enable;
        self
    }

    #[allow(dead_code)]
    pub fn with_strikethrough(mut self, enable: bool) -> Self {
        self.enable_strikethrough = enable;
        self
    }

    #[allow(dead_code)]
    pub fn with_tasklists(mut self, enable: bool) -> Self {
        self.enable_tasklists = enable;
        self
    }

    #[allow(dead_code)]
    pub fn with_footnotes(mut self, enable: bool) -> Self {
        self.enable_footnotes = enable;
        self
    }

    #[allow(dead_code)]
    pub fn with_smart_punctuation(mut self, enable: bool) -> Self {
        self.enable_smart_punctuation = enable;
        self
    }

    #[allow(dead_code)]
    pub fn with_custom_css_class(mut self, element: String, class: String) -> Self {
        self.custom_css_classes.insert(element, class);
        self
    }

    pub fn render_content(&self, content: &PageContent) -> Result<String> {
        match content.content_format {
            ContentFormat::Markdown => self.render_markdown(&content.content),
            ContentFormat::Html => Ok(self.sanitize_html(&content.content)?),
            ContentFormat::PlainText => Ok(self.render_plain_text(&content.content)),
        }
    }

    pub fn render_markdown(&self, markdown: &str) -> Result<String> {
        let mut options = Options::empty();

        if self.enable_tables {
            options.insert(Options::ENABLE_TABLES);
        }
        if self.enable_strikethrough {
            options.insert(Options::ENABLE_STRIKETHROUGH);
        }
        if self.enable_tasklists {
            options.insert(Options::ENABLE_TASKLISTS);
        }
        if self.enable_footnotes {
            options.insert(Options::ENABLE_FOOTNOTES);
        }
        if self.enable_smart_punctuation {
            options.insert(Options::ENABLE_SMART_PUNCTUATION);
        }

        let parser = Parser::new_ext(markdown, options);
        let events: Vec<Event> = parser.collect();
        let events = self.add_custom_css_classes(events);

        let mut html_output = String::new();
        html::push_html(&mut html_output, events.into_iter());

        Ok(html_output)
    }

    // Removed: add_syntax_highlighting and highlight_code methods
    // Now using client-side highlight.js for syntax highlighting

    fn add_custom_css_classes<'a>(&self, events: Vec<Event<'a>>) -> Vec<Event<'a>> {
        let mut processed_events = Vec::new();

        for event in events {
            match event {
                Event::Start(Tag::Heading { level, .. }) => {
                    let class = self
                        .custom_css_classes
                        .get(&format!("h{}", level as u8))
                        .or_else(|| self.custom_css_classes.get("heading"))
                        .cloned()
                        .unwrap_or_default();

                    if !class.is_empty() {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("<h{} class=\"{}\">", level as u8, class).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(Event::Start(Tag::Heading {
                            level,
                            id: None,
                            classes: vec![],
                            attrs: vec![],
                        }));
                    }
                }
                Event::End(TagEnd::Heading(level)) => {
                    if self
                        .custom_css_classes
                        .contains_key(&format!("h{}", level as u8))
                        || self.custom_css_classes.contains_key("heading")
                    {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("</h{}>", level as u8).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(Event::End(TagEnd::Heading(level)));
                    }
                }
                Event::Start(Tag::Paragraph) => {
                    if let Some(class) = self.custom_css_classes.get("paragraph") {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("<p class=\"{}\">", class).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::End(TagEnd::Paragraph) => {
                    if self.custom_css_classes.contains_key("paragraph") {
                        processed_events.push(Event::Html(CowStr::Borrowed("</p>")));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::Start(Tag::BlockQuote(_)) => {
                    if let Some(class) = self.custom_css_classes.get("blockquote") {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("<blockquote class=\"{}\">", class).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::End(TagEnd::BlockQuote(_)) => {
                    if self.custom_css_classes.contains_key("blockquote") {
                        processed_events.push(Event::Html(CowStr::Borrowed("</blockquote>")));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::Start(Tag::List(_)) => {
                    if let Some(class) = self.custom_css_classes.get("list") {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("<ul class=\"{}\">", class).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::End(TagEnd::List(_)) => {
                    if self.custom_css_classes.contains_key("list") {
                        processed_events.push(Event::Html(CowStr::Borrowed("</ul>")));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::Start(Tag::Table(_)) => {
                    if let Some(class) = self.custom_css_classes.get("table") {
                        processed_events.push(Event::Html(CowStr::Boxed(
                            format!("<table class=\"{}\">", class).into_boxed_str(),
                        )));
                    } else {
                        processed_events.push(event);
                    }
                }
                Event::End(TagEnd::Table) => {
                    if self.custom_css_classes.contains_key("table") {
                        processed_events.push(Event::Html(CowStr::Borrowed("</table>")));
                    } else {
                        processed_events.push(event);
                    }
                }
                _ => processed_events.push(event),
            }
        }

        processed_events
    }

    fn sanitize_html(&self, html: &str) -> Result<String> {
        // Basic HTML sanitization - in a real implementation, use a proper HTML
        // sanitizer like ammonia or bleach
        let mut sanitized = html.to_string();

        // Remove potentially dangerous tags
        let dangerous_tags = [
            "<script",
            "</script>",
            "<iframe",
            "</iframe>",
            "<object",
            "</object>",
            "<embed",
            "</embed>",
            "<applet",
            "</applet>",
            "<form",
            "</form>",
            "<input",
            "<button",
            "<select",
            "<textarea",
            "<link",
            "<style",
            "</style>",
        ];

        for tag in &dangerous_tags {
            sanitized = sanitized.replace(tag, "");
        }

        // Remove javascript: URLs
        sanitized = sanitized.replace("javascript:", "");

        // Remove on* event attributes
        let event_attrs = [
            "onclick",
            "onload",
            "onmouseover",
            "onmouseout",
            "onkeydown",
            "onkeyup",
            "onchange",
            "onsubmit",
            "onerror",
            "onblur",
            "onfocus",
        ];

        for attr in &event_attrs {
            // Simple regex replacement - in production, use proper HTML parsing
            sanitized = sanitized.replace(&format!(" {}=", attr), " data-removed=");
        }

        Ok(sanitized)
    }

    fn render_plain_text(&self, text: &str) -> String {
        // Convert plain text to HTML with proper line breaks
        let escaped = html_escape(text);
        escaped.replace('\n', "<br>")
    }

    pub fn extract_excerpt(&self, content: &PageContent, max_length: usize) -> String {
        if let Some(excerpt) = &content.excerpt {
            return excerpt.clone();
        }

        let text = match content.content_format {
            ContentFormat::Markdown => self.markdown_to_text(&content.content),
            ContentFormat::Html => self.html_to_text(&content.content),
            ContentFormat::PlainText => content.content.clone(),
        };

        if text.len() <= max_length {
            text
        } else {
            let truncated = &text[..max_length];
            if let Some(last_space) = truncated.rfind(' ') {
                format!("{}...", &truncated[..last_space])
            } else {
                format!("{}...", truncated)
            }
        }
    }

    fn markdown_to_text(&self, markdown: &str) -> String {
        let parser = Parser::new(markdown);
        let mut text = String::new();

        for event in parser {
            match event {
                Event::Text(content) => text.push_str(&content),
                Event::Code(content) => text.push_str(&content),
                Event::SoftBreak | Event::HardBreak => text.push(' '),
                _ => {}
            }
        }

        text
    }

    fn html_to_text(&self, html: &str) -> String {
        // Simple HTML to text conversion - in production, use proper HTML parsing
        let mut text = html.to_string();

        // Remove HTML tags
        while let Some(start) = text.find('<') {
            if let Some(end) = text[start..].find('>') {
                text.replace_range(start..start + end + 1, "");
            } else {
                break;
            }
        }

        // Decode HTML entities
        text = text.replace("&amp;", "&");
        text = text.replace("&lt;", "<");
        text = text.replace("&gt;", ">");
        text = text.replace("&quot;", "\"");
        text = text.replace("&#39;", "'");
        text = text.replace("&nbsp;", " ");

        // Normalize whitespace
        text.split_whitespace().collect::<Vec<_>>().join(" ")
    }

    pub fn generate_table_of_contents(&self, content: &PageContent) -> Result<Vec<TocEntry>> {
        if !matches!(content.content_format, ContentFormat::Markdown) {
            return Ok(Vec::new());
        }

        let parser = Parser::new(&content.content);
        let mut toc = Vec::new();
        let mut current_text = String::new();
        let mut in_heading = false;
        let mut heading_level = 0;

        for event in parser {
            match event {
                Event::Start(Tag::Heading { level, .. }) => {
                    in_heading = true;
                    heading_level = level as u8;
                    current_text.clear();
                }
                Event::End(TagEnd::Heading(_)) => {
                    if in_heading {
                        let anchor = slugify(&current_text);
                        toc.push(TocEntry {
                            level: heading_level,
                            title: current_text.clone(),
                            anchor,
                        });
                        in_heading = false;
                    }
                }
                Event::Text(text) => {
                    if in_heading {
                        current_text.push_str(&text);
                    }
                }
                _ => {}
            }
        }

        Ok(toc)
    }
}

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

#[derive(Debug, Clone, serde::Serialize)]
pub struct TocEntry {
    pub level: u8,
    pub title: String,
    pub anchor: String,
}

fn html_escape(text: &str) -> String {
    text.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&#39;")
}

fn slugify(text: &str) -> String {
    text.to_lowercase()
        .chars()
        .map(|c| {
            if c.is_alphanumeric() {
                c
            } else if c.is_whitespace() || c == '-' || c == '_' {
                '-'
            } else {
                '\0'
            }
        })
        .filter(|&c| c != '\0')
        .collect::<String>()
        .split('-')
        .filter(|s| !s.is_empty())
        .collect::<Vec<_>>()
        .join("-")
}

#[cfg(test)]
mod tests {
    use rustelo_core_lib::fluent::{ContentFormat, ContentType, PageContent};

    use super::*;

    fn create_test_content(content: &str, format: ContentFormat) -> PageContent {
        PageContent::new(
            "test-slug".to_string(),
            "Test Title".to_string(),
            "test-name".to_string(),
            ContentType::Page,
            content.to_string(),
            "test-container".to_string(),
            None,
        )
        .with_content_format(format)
    }

    #[test]
    fn test_markdown_rendering() {
        let renderer = ContentRenderer::new_for_testing();
        let content = create_test_content(
            "# Hello World\n\nThis is **bold** text.",
            ContentFormat::Markdown,
        );

        let result = renderer.render_content(&content).unwrap();
        assert!(result.contains("<h1>Hello World</h1>"));
        assert!(result.contains("<strong>bold</strong>"));
    }

    #[test]
    fn test_html_sanitization() {
        let renderer = ContentRenderer::new_for_testing();
        let content = create_test_content(
            "<p>Safe content</p><script>alert('xss')</script>",
            ContentFormat::Html,
        );

        let result = renderer.render_content(&content).unwrap();
        assert!(result.contains("<p>Safe content</p>"));
        assert!(!result.contains("<script>"));
    }

    #[test]
    fn test_plain_text_rendering() {
        let renderer = ContentRenderer::new_for_testing();
        let content = create_test_content("Line 1\nLine 2", ContentFormat::PlainText);

        let result = renderer.render_content(&content).unwrap();
        assert!(result.contains("Line 1<br>Line 2"));
    }

    #[test]
    fn test_excerpt_extraction() {
        let renderer = ContentRenderer::new_for_testing();
        let content = create_test_content(
            "This is a long text that should be truncated at some point.",
            ContentFormat::PlainText,
        );

        let excerpt = renderer.extract_excerpt(&content, 20);
        assert!(excerpt.len() <= 23); // 20 + "..."
        assert!(excerpt.ends_with("..."));
    }

    #[test]
    fn test_table_of_contents() {
        let renderer = ContentRenderer::new_for_testing();
        let markdown = "# Chapter 1\n\n## Section 1.1\n\n### Subsection 1.1.1\n\n## Section 1.2";
        let content = create_test_content(markdown, ContentFormat::Markdown);

        let toc = renderer.generate_table_of_contents(&content).unwrap();
        assert_eq!(toc.len(), 4);
        assert_eq!(toc[0].level, 1);
        assert_eq!(toc[0].title, "Chapter 1");
        assert_eq!(toc[1].level, 2);
        assert_eq!(toc[1].title, "Section 1.1");
    }

    #[test]
    fn test_custom_css_classes() {
        let renderer = ContentRenderer::new_for_testing()
            .with_custom_css_class("heading".to_string(), "my-heading".to_string())
            .with_custom_css_class("paragraph".to_string(), "my-paragraph".to_string());

        let markdown = "# Title\n\nParagraph text.";
        let result = renderer.render_markdown(markdown).unwrap();

        assert!(result.contains("class=\"my-heading\""));
        assert!(result.contains("class=\"my-paragraph\""));
    }

    #[test]
    fn test_slugify() {
        assert_eq!(slugify("Hello World"), "hello-world");
        assert_eq!(slugify("Test Title 123"), "test-title-123");
        assert_eq!(slugify("Special@#$%Characters"), "specialcharacters");
    }

    #[test]
    fn test_html_escape() {
        assert_eq!(
            html_escape("<script>alert('xss')</script>"),
            "&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;"
        );
    }
}
