use crate::{error::HtmlError, extract_front_matter, Result};
use mdx_gen::{process_markdown, ComrakOptions, MarkdownOptions};
use regex::Regex;
use std::error::Error;
pub fn generate_html(
    markdown: &str,
    _config: &crate::HtmlConfig,
) -> Result<String> {
    markdown_to_html_with_extensions(markdown)
}
pub fn markdown_to_html_with_extensions(
    markdown: &str,
) -> Result<String> {
    let content_without_front_matter = extract_front_matter(markdown)
        .unwrap_or_else(|_| markdown.to_string());
    let markdown_with_classes =
        add_custom_classes(&content_without_front_matter);
    let markdown_with_images =
        process_images_with_classes(&markdown_with_classes);
    let mut comrak_options = ComrakOptions::default();
    comrak_options.extension.strikethrough = true;
    comrak_options.extension.table = true;
    comrak_options.extension.autolink = true;
    comrak_options.extension.tasklist = true;
    comrak_options.extension.superscript = true;
    comrak_options.render.unsafe_ = true; comrak_options.render.escape = false;
    let options =
        MarkdownOptions::default().with_comrak_options(comrak_options);
    match process_markdown(&markdown_with_images, &options) {
        Ok(html_output) => Ok(html_output),
        Err(err) => {
            Err(HtmlError::markdown_conversion(err.to_string(), None))
        }
    }
}
fn add_custom_classes(markdown: &str) -> String {
    let re = Regex::new(r":::(\w+)\n([\s\S]*?)\n:::").unwrap();
    re.replace_all(markdown, |caps: ®ex::Captures| {
        let class_name = &caps[1];
        let block_content = &caps[2];
        let inline_html = match process_markdown_inline(block_content) {
            Ok(html) => html,
            Err(err) => {
                eprintln!(
                    "Warning: failed to parse inline block content. Using raw text. Error: {err}"
                );
                block_content.to_string()
            }
        };
        format!("<div class=\"{}\">{}</div>", class_name, inline_html)
    })
    .to_string()
}
pub fn process_markdown_inline(
    content: &str,
) -> std::result::Result<String, Box<dyn Error>> {
    let mut comrak_opts = ComrakOptions::default();
    comrak_opts.extension.strikethrough = true;
    comrak_opts.extension.table = true;
    comrak_opts.extension.autolink = true;
    comrak_opts.extension.tasklist = true;
    comrak_opts.extension.superscript = true;
    comrak_opts.render.unsafe_ = true; comrak_opts.render.escape = false;
    let options =
        MarkdownOptions::default().with_comrak_options(comrak_opts);
    let inline_html = process_markdown(content, &options)?;
    Ok(inline_html)
}
fn process_images_with_classes(markdown: &str) -> String {
    let re =
        Regex::new(r#"!\[(.*?)\]\((.*?)\)\.class="(.*?)""#).unwrap();
    re.replace_all(markdown, |caps: ®ex::Captures| {
        format!(
            r#"<img src="{}" alt="{}" class="{}" />"#,
            &caps[2], &caps[1], &caps[3], )
    })
    .to_string()
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::HtmlConfig;
    #[test]
    fn test_generate_html_basic() {
        let markdown = "# Hello, world!\n\nThis is a test.";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<h1>Hello, world!</h1>"));
        assert!(html.contains("<p>This is a test.</p>"));
    }
    #[test]
    fn test_markdown_to_html_with_extensions() {
        let markdown = r"
| Header 1 | Header 2 |
| -------- | -------- |
| Row 1    | Row 2    |
";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("{}", html);
        assert!(html.contains("<div class=\"table-responsive\"><table class=\"table\">"), "Table element not found");
        assert!(
            html.contains("<th>Header 1</th>"),
            "Table header not found"
        );
        assert!(
            html.contains("<td class=\"text-left\">Row 1</td>"),
            "Table row not found"
        );
    }
    #[test]
    fn test_generate_html_empty() {
        let markdown = "";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.is_empty());
    }
    #[test]
    fn test_generate_html_invalid_markdown() {
        let markdown = "# Unclosed header\nSome **unclosed bold";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("{}", html);
        assert!(
            html.contains("<h1>Unclosed header</h1>"),
            "Header not found"
        );
        assert!(
            html.contains("<p>Some **unclosed bold</p>"),
            "Unclosed bold tag not properly handled"
        );
    }
    #[test]
    fn test_generate_html_complex() {
        let markdown = r#"
# Header
## Subheader
Some `inline code` and a [link](https://example.com).
```rust
fn main() {
    println!("Hello, world!");
}
```
1. First item
2. Second item
"#;
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("{}", html);
        assert!(
            html.contains("<h1>Header</h1>"),
            "H1 Header not found"
        );
        assert!(
            html.contains("<h2>Subheader</h2>"),
            "H2 Header not found"
        );
        assert!(
            html.contains("<code>inline code</code>"),
            "Inline code not found"
        );
        assert!(
            html.contains(r#"<a href="https://example.com">link</a>"#),
            "Link not found"
        );
        assert!(
            html.contains(r#"<code class="language-rust">"#),
            "Code block with language-rust class not found"
        );
        assert!(
            html.contains(r#"<span style="color:#b48ead;">fn </span>"#),
            "`fn` keyword with syntax highlighting not found"
        );
        assert!(
            html.contains(
                r#"<span style="color:#8fa1b3;">main</span>"#
            ),
            "`main` function name with syntax highlighting not found"
        );
        assert!(
            html.contains("<li>First item</li>"),
            "First item not found"
        );
        assert!(
            html.contains("<li>Second item</li>"),
            "Second item not found"
        );
    }
    #[test]
    fn test_generate_html_with_valid_front_matter() {
        let markdown = r#"---
title: Test
author: Jane Doe
---
# Hello, world!"#;
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<h1>Hello, world!</h1>"));
    }
    #[test]
    fn test_generate_html_with_invalid_front_matter() {
        let markdown = r#"---
title Test
author: Jane Doe
---
# Hello, world!"#;
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(
            result.is_ok(),
            "Invalid front matter should be ignored"
        );
        let html = result.unwrap();
        assert!(html.contains("<h1>Hello, world!</h1>"));
    }
    #[test]
    fn test_generate_html_large_input() {
        let markdown = "# Large Markdown\n\n".repeat(10_000);
        let config = HtmlConfig::default();
        let result = generate_html(&markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<h1>Large Markdown</h1>"));
    }
    #[test]
    fn test_generate_html_with_custom_markdown_options() {
        let markdown = "**Bold text**";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<strong>Bold text</strong>"));
    }
    #[test]
    fn test_generate_html_with_unsupported_elements() {
        let markdown = "::: custom_block\nContent\n:::";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("::: custom_block"));
    }
    #[test]
    fn test_markdown_to_html_with_conversion_error() {
        let markdown = "# Unclosed header\nSome **unclosed bold";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<p>Some **unclosed bold</p>"));
    }
    #[test]
    fn test_generate_html_whitespace_only() {
        let markdown = "   \n   ";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.is_empty(),
            "Whitespace-only Markdown should produce empty HTML"
        );
    }
    #[test]
    fn test_markdown_to_html_with_custom_comrak_options() {
        let markdown = "^^Superscript^^\n\n| Header 1 | Header 2 |\n| -------- | -------- |\n| Row 1    | Row 2    |";
        let mut comrak_options = ComrakOptions::default();
        comrak_options.extension.superscript = true;
        comrak_options.extension.table = true; let options = MarkdownOptions::default()
            .with_comrak_options(comrak_options.clone());
        let content_without_front_matter =
            extract_front_matter(markdown)
                .unwrap_or(markdown.to_string());
        println!("Comrak options: {:?}", comrak_options);
        let result =
            process_markdown(&content_without_front_matter, &options);
        match result {
            Ok(ref html) => {
                assert!(
                    html.contains("<sup>Superscript</sup>"),
                    "Superscript not found in HTML output"
                );
                assert!(
                    html.contains("<table"),
                    "Table element not found in HTML output"
                );
            }
            Err(err) => {
                eprintln!("Markdown processing error: {:?}", err);
                panic!("Failed to process Markdown with custom ComrakOptions");
            }
        }
    }
    #[test]
    fn test_generate_html_with_default_config() {
        let markdown = "# Default Configuration Test";
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<h1>Default Configuration Test</h1>"));
    }
    #[test]
    fn test_generate_html_with_custom_front_matter_delimiter() {
        let markdown = r#";;;;
title: Custom
author: John Doe
;;;;
# Custom Front Matter Delimiter"#;
        let config = HtmlConfig::default();
        let result = generate_html(markdown, &config);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<h1>Custom Front Matter Delimiter</h1>"));
    }
    #[test]
    fn test_generate_html_with_task_list() {
        let markdown = r"
- [x] Task 1
- [ ] Task 2
";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("Generated HTML:\n{}", html);
        assert!(
        html.contains(r#"<li><input type="checkbox" checked="" disabled="" /> Task 1</li>"#),
        "Task 1 checkbox not rendered as expected"
    );
        assert!(
        html.contains(r#"<li><input type="checkbox" disabled="" /> Task 2</li>"#),
        "Task 2 checkbox not rendered as expected"
    );
    }
    #[test]
    fn test_generate_html_with_large_table() {
        let header =
            "| Header 1 | Header 2 |\n| -------- | -------- |\n";
        let rows = "| Row 1    | Row 2    |\n".repeat(1000);
        let markdown = format!("{}{}", header, rows);
        let result = markdown_to_html_with_extensions(&markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        let row_count = html.matches("<tr>").count();
        assert_eq!(
            row_count, 1001,
            "Incorrect number of rows: {}",
            row_count
        ); }
    #[test]
    fn test_generate_html_with_special_characters() {
        let markdown = r#"Markdown with special characters: <, >, &, "quote", 'single-quote'."#;
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<"), "Less than sign not escaped");
        assert!(html.contains(">"), "Greater than sign not escaped");
        assert!(html.contains("&"), "Ampersand not escaped");
        assert!(html.contains("""), "Double quote not escaped");
        assert!(
            html.contains("'") || html.contains("'"),
            "Single quote not handled as expected"
        );
    }
    #[test]
    fn test_generate_html_with_invalid_markdown_syntax() {
        let markdown =
            r"# Invalid Markdown <unexpected> [bad](url <here)";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("Generated HTML:\n{}", html);
        assert!(
            html.contains("<unexpected>"),
            "Raw HTML tags like <unexpected> should not be escaped"
        );
        assert!(
            html.contains("<here>") || html.contains("<here)"),
            "Angle brackets in links should be escaped for safety"
        );
        assert!(
        html.contains("<h1>Invalid Markdown <unexpected> [bad](url <here)</h1>"),
        "Header not rendered correctly or content not properly handled"
    );
    }
    #[test]
    fn test_generate_html_mixed_markdown() {
        let markdown = r"# Valid Header
Some **bold text** followed by invalid Markdown:
~~strikethrough~~ without a closing tag.";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.contains("<h1>Valid Header</h1>"),
            "Header not found"
        );
        assert!(
            html.contains("<strong>bold text</strong>"),
            "Bold text not rendered correctly"
        );
        assert!(
            html.contains("<del>strikethrough</del>"),
            "Strikethrough not rendered correctly"
        );
    }
    #[test]
    fn test_generate_html_deeply_nested_content() {
        let markdown = r"
1. Level 1
    1.1. Level 2
        1.1.1. Level 3
            1.1.1.1. Level 4
";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(html.contains("<ol>"), "Ordered list not rendered");
        assert!(html.contains("<li>Level 1"), "Level 1 not rendered");
        assert!(
            html.contains("1.1.1.1. Level 4"),
            "Deeply nested levels not rendered correctly"
        );
    }
    #[test]
    fn test_generate_html_with_raw_html() {
        let markdown = r"
# Header with HTML
<p>This is a paragraph with <strong>HTML</strong>.</p>
";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.contains("<p>This is a paragraph with <strong>HTML</strong>.</p>"),
            "Raw HTML content not preserved in output"
        );
    }
    #[test]
    fn test_generate_html_invalid_front_matter_handling() {
        let markdown = "---
key_without_value
another_key: valid
---
# Markdown Content
";
        let result = generate_html(markdown, &HtmlConfig::default());
        assert!(
            result.is_ok(),
            "Invalid front matter should not cause an error"
        );
        let html = result.unwrap();
        assert!(
            html.contains("<h1>Markdown Content</h1>"),
            "Content not processed correctly"
        );
    }
    #[test]
    fn test_generate_html_large_front_matter() {
        let front_matter = "---\n".to_owned()
            + &"key: value\n".repeat(10_000)
            + "---\n# Content";
        let result =
            generate_html(&front_matter, &HtmlConfig::default());
        assert!(
            result.is_ok(),
            "Large front matter should be handled gracefully"
        );
        let html = result.unwrap();
        assert!(
            html.contains("<h1>Content</h1>"),
            "Content not rendered correctly"
        );
    }
    #[test]
    fn test_generate_html_with_long_lines() {
        let markdown = "A ".repeat(10_000);
        let result = markdown_to_html_with_extensions(&markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.contains("A A A A"),
            "Long consecutive lines should be rendered properly"
        );
    }
    #[test]
    fn test_markdown_with_custom_classes() {
        let markdown = r":::note
This is a note with a custom class.
:::";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok(), "Markdown conversion should not fail.");
        let html = result.unwrap();
        println!("HTML:\n{}", html);
        assert!(
            html.contains(r#"<div class="note">"#),
            "Custom block should wrap in <div class=\"note\">"
        );
        assert!(
            html.contains("This is a note with a custom class."),
            "Block text is missing or incorrectly rendered"
        );
    }
    #[test]
    fn test_markdown_with_custom_blocks_and_images() {
        let markdown = ".class=\"img-fluid\"";
        let result = markdown_to_html_with_extensions(markdown);
        assert!(result.is_ok());
        let html = result.unwrap();
        println!("{}", html);
        assert!(
        html.contains(r#"<img src="https://example.com/image.webp" alt="A very tall building" class="img-fluid" />"#),
        "First image not rendered correctly"
    );
    }
    #[test]
    fn test_empty_front_matter_handling() {
        let markdown = "---\n---\n# Content";
        let result = generate_html(markdown, &HtmlConfig::default());
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.contains("<h1>Content</h1>"),
            "Content should be processed correctly"
        );
    }
    #[test]
    fn test_invalid_image_syntax() {
        let markdown = "![Image with missing URL]()";
        let result = process_images_with_classes(markdown);
        assert_eq!(
            result, markdown,
            "Invalid image syntax should remain unchanged"
        );
    }
    #[test]
    fn test_incorrect_front_matter_delimiters() {
        let markdown = ";;;\ntitle: Test\n---\n# Header";
        let result = generate_html(markdown, &HtmlConfig::default());
        assert!(result.is_ok());
        let html = result.unwrap();
        assert!(
            html.contains("<h1>Header</h1>"),
            "Header should be processed correctly"
        );
    }
    #[cfg(test)]
    mod missing_scenarios_tests {
        use super::*;
        #[test]
        fn test_triple_colon_warning_with_bold() {
            let markdown = r":::warning
**Caution:** This operation is sensitive.
:::";
            let result = markdown_to_html_with_extensions(markdown);
            assert!(
                result.is_ok(),
                "Markdown conversion should succeed."
            );
            let html = result.unwrap();
            println!("HTML:\n{}", html);
            assert!(
                html.contains(r#"<div class="warning">"#),
                "Expected <div class=\"warning\"> wrapping the block"
            );
            assert!(html.contains("<strong>Caution:</strong>"),
            "Expected inline bold text to become <strong>Caution:</strong>");
        }
        #[test]
        fn test_multiple_triple_colon_blocks() {
            let markdown = r":::note
**Note:** First block
:::
:::warning
**Warning:** Second block
:::";
            let result = markdown_to_html_with_extensions(markdown);
            assert!(
                result.is_ok(),
                "Markdown conversion should succeed."
            );
            let html = result.unwrap();
            println!("HTML:\n{}", html);
            assert!(
                html.contains(r#"<div class="note">"#),
                "Missing <div class=\"note\"> for the first block"
            );
            assert!(
                html.contains(r#"<div class="warning">"#),
                "Missing <div class=\"warning\"> for the second block"
            );
            assert!(
                html.contains("<strong>Note:</strong>"),
                "Bold text in the note block not parsed"
            );
            assert!(
                html.contains("<strong>Warning:</strong>"),
                "Bold text in the warning block not parsed"
            );
        }
        #[test]
        fn test_triple_colon_block_multi_paragraph() {
            let markdown = r":::note
**Paragraph 1:** This is the first paragraph.
This is the second paragraph, also with **bold** text.
:::";
            let result = markdown_to_html_with_extensions(markdown);
            assert!(
                result.is_ok(),
                "Markdown conversion should succeed."
            );
            let html = result.unwrap();
            println!("HTML:\n{}", html);
            assert!(
                html.contains("<strong>Paragraph 1:</strong>"),
                "Inline bold text not parsed in the first paragraph"
            );
            assert!(html.contains("second paragraph, also with <strong>bold</strong> text"),
            "Inline bold text not parsed in the second paragraph");
        }
        #[test]
        fn test_triple_colon_block_forcing_inline_error() {
            let markdown = r":::error
This block tries < to break > inline parsing & [some link (unclosed).
:::";
            let result = markdown_to_html_with_extensions(markdown);
            assert!(
                result.is_ok(),
                "We won't forcibly error, but let's see the output."
            );
            let html = result.unwrap();
            println!("HTML:\n{}", html);
            assert!(
                html.contains(r#"<div class="error">"#),
                "Block div not found for 'error' class"
            );
            assert!(html.contains("This block tries ") || html.contains("Warning: failed to parse inline block content"),
            "Expected either parsed content or a fallback error message");
        }
    }
}