"""HTML builder — converts a Deck into a self-contained HTML file."""

from __future__ import annotations

from pathlib import Path
from string import Template

from markdown_it import MarkdownIt
from mdit_py_plugins.dollarmath import dollarmath_plugin

from colloquium.deck import Deck
from colloquium.slide import Slide


def _get_theme_path(theme: str = "default") -> Path:
    """Get the path to a theme directory."""
    themes_dir = Path(__file__).parent / "themes" / theme
    if not themes_dir.exists():
        themes_dir = Path(__file__).parent / "themes" / "default"
    return themes_dir


def _read_theme_css(theme: str = "default") -> str:
    """Read the theme CSS file."""
    return (_get_theme_path(theme) / "theme.css").read_text(encoding="utf-8")


def _read_presentation_js(theme: str = "default") -> str:
    """Read the presentation JS file."""
    return (_get_theme_path(theme) / "presentation.js").read_text(encoding="utf-8")


def _create_md_renderer() -> MarkdownIt:
    """Create a markdown-it renderer with math support."""
    md = MarkdownIt("commonmark", {"html": True, "typographer": True})
    dollarmath_plugin(md, double_inline=True)
    return md


def _render_markdown(text: str, md: MarkdownIt) -> str:
    """Render markdown text to HTML."""
    if not text:
        return ""
    return md.render(text)


def _build_slide_html(slide: Slide, index: int, md: MarkdownIt) -> str:
    """Build the HTML for a single slide."""
    # CSS classes
    classes = ["slide", f"slide--{slide.layout}"]
    if index == 0:
        classes.append("active")
    classes.extend(slide.classes)

    class_str = " ".join(classes)

    # Style attribute
    style_attr = f' style="{slide.style}"' if slide.style else ""

    # Slide content
    parts = []
    if slide.title:
        tag = "h1" if slide.is_title_slide else "h2"
        parts.append(f"<{tag}>{slide.title}</{tag}>")

    if slide.content:
        rendered = _render_markdown(slide.content, md)
        parts.append(f'<div class="slide-content">{rendered}</div>')

    inner = "\n".join(parts)

    return f'<section class="{class_str}"{style_attr} data-index="{index}">\n{inner}\n</section>'


# HTML template — uses $-style substitution
_HTML_TEMPLATE = Template("""\
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>$title</title>

<!-- KaTeX for LaTeX math -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/contrib/auto-render.min.js"></script>

<!-- highlight.js for code syntax highlighting -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/github.min.css">
<script defer src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>

<style>
$theme_css
$custom_css
</style>
</head>
<body>
<div class="colloquium-deck">
$slides_html
</div>

<div class="colloquium-nav">
    <span class="colloquium-counter">1 / $total_slides</span>
</div>

<div class="colloquium-progress">
    <div class="colloquium-progress-bar" style="width: 0%"></div>
</div>

<script>
$presentation_js
</script>

<script>
// Initialize KaTeX auto-render after load
document.addEventListener("DOMContentLoaded", function() {
    if (typeof renderMathInElement !== "undefined") {
        renderMathInElement(document.body, {
            delimiters: [
                {left: "$$$$", right: "$$$$", display: true},
                {left: "$$", right: "$$", display: false},
                {left: "\\\\(", right: "\\\\)", display: false},
                {left: "\\\\[", right: "\\\\]", display: true}
            ],
            throwOnError: false
        });
    }
    // Initialize highlight.js
    if (typeof hljs !== "undefined") {
        hljs.highlightAll();
    }
});
</script>
</body>
</html>
""")


def build_deck(deck: Deck) -> str:
    """Build a Deck into a self-contained HTML string."""
    md = _create_md_renderer()
    theme_css = _read_theme_css(deck.theme)
    presentation_js = _read_presentation_js(deck.theme)

    slides_html_parts = []
    for i, slide in enumerate(deck.slides):
        slides_html_parts.append(_build_slide_html(slide, i, md))

    slides_html = "\n\n".join(slides_html_parts)

    return _HTML_TEMPLATE.substitute(
        title=deck.title,
        theme_css=theme_css,
        custom_css=deck.custom_css,
        slides_html=slides_html,
        total_slides=len(deck.slides),
        presentation_js=presentation_js,
    )


def build_file(input_path: str, output_path: str | None = None) -> str:
    """Build a markdown file into an HTML file. Returns the output path."""
    from colloquium.parse import parse_file

    deck = parse_file(input_path)
    html = build_deck(deck)

    if output_path is None:
        output_path = str(Path(input_path).with_suffix(".html"))

    Path(output_path).parent.mkdir(parents=True, exist_ok=True)
    Path(output_path).write_text(html, encoding="utf-8")
    return output_path
