Documentation
//! MDX: A minimal, elegant Markdown to UI renderer with custom components

pub mod component;
pub mod config;
pub mod error;
pub mod parser;
pub mod renderer;
pub mod theme;

pub use component::{Component, ComponentRegistry};
pub use config::Config;
pub use error::Error;
pub use parser::{parse, InlineNode, Node, ParseOptions, ParsedDocument};
pub use renderer::{render, RenderOptions};
pub use theme::{Theme, ThemeOptions};

/// Renders a Markdown string to HTML
pub fn render_to_html(markdown: &str, config: Option<Config>) -> Result<String, error::Error> {
    let config = config.unwrap_or_default();
    let parse_options = ParseOptions::from(&config);
    let registry = ComponentRegistry::default();

    // Preprocess the markdown to handle custom component syntax
    let preprocessed_markdown = preprocess_components(markdown);

    let ast = parser::parse(&preprocessed_markdown, &parse_options)?;
    renderer::render(&ast, &registry, &config)
}

/// Renders a Markdown string to HTML with custom component registry
pub fn render_to_html_with_registry(
    markdown: &str,
    config: Option<Config>,
    registry: &ComponentRegistry,
) -> Result<String, error::Error> {
    let config = config.unwrap_or_default();
    let parse_options = ParseOptions::from(&config);

    // Preprocess the markdown to handle custom component syntax
    let preprocessed_markdown = preprocess_components(markdown);

    let ast = parser::parse(&preprocessed_markdown, &parse_options)?;
    renderer::render(&ast, registry, &config)
}

/// Preprocesses markdown content to transform custom component syntax to HTML
fn preprocess_components(markdown: &str) -> String {
    use regex::Regex;

    // Handle components with :: syntax
    let component_regex = Regex::new(r"(?m)^::([a-zA-Z0-9_-]+)(\{([^}]*)\})?\s*$").unwrap();
    let component_end_regex = Regex::new(r"(?m)^::\s*$").unwrap();

    // Handle nested components with ::: syntax
    let nested_component_regex = Regex::new(r"(?m)^:::([a-zA-Z0-9_-]+)(\{([^}]*)\})?\s*$").unwrap();
    let nested_component_end_regex = Regex::new(r"(?m)^:::\s*$").unwrap();

    // First, replace all component starts with HTML comments with metadata
    let mut result = component_regex
        .replace_all(markdown, |caps: &regex::Captures| {
            let component_name = &caps[1];
            let attributes = caps.get(3).map_or("", |m| m.as_str());
            format!(
                "<!-- component_start:{}:{} -->\n",
                component_name, attributes
            )
        })
        .to_string();

    // Replace all component ends with HTML comments
    result = component_end_regex
        .replace_all(&result, |_: &regex::Captures| {
            "<!-- component_end -->\n".to_string()
        })
        .to_string();

    // Handle nested components
    result = nested_component_regex
        .replace_all(&result, |caps: &regex::Captures| {
            let component_name = &caps[1];
            let attributes = caps.get(3).map_or("", |m| m.as_str());
            format!(
                "<!-- nested_component_start:{}:{} -->\n",
                component_name, attributes
            )
        })
        .to_string();

    // Replace all nested component ends with HTML comments
    result = nested_component_end_regex
        .replace_all(&result, |_: &regex::Captures| {
            "<!-- nested_component_end -->\n".to_string()
        })
        .to_string();

    result
}

/// Renders a Markdown file to HTML
pub fn render_file(path: &str, config: Option<Config>) -> Result<String, error::Error> {
    use std::fs;
    let markdown = fs::read_to_string(path).map_err(error::Error::IoError)?;
    render_to_html(&markdown, config)
}

/// Renders a Markdown file to an HTML file
pub fn render_file_to_file(
    input: &str,
    output: &str,
    config: Option<Config>,
) -> Result<(), error::Error> {
    use std::fs;
    let html = render_file(input, config)?;
    fs::write(output, html).map_err(error::Error::IoError)?;
    Ok(())
}