"""Unit tests for the Lexical JSON → HTML renderer."""
from __future__ import annotations

import pytest

from blog.bp.blog.ghost.lexical_renderer import (
    render_lexical, _wrap_format, _align_style,
    _FORMAT_BOLD, _FORMAT_ITALIC, _FORMAT_STRIKETHROUGH,
    _FORMAT_UNDERLINE, _FORMAT_CODE, _FORMAT_SUBSCRIPT,
    _FORMAT_SUPERSCRIPT, _FORMAT_HIGHLIGHT,
)


# ---------------------------------------------------------------------------
# _wrap_format
# ---------------------------------------------------------------------------

class TestWrapFormat:
    def test_no_format(self):
        assert _wrap_format("hello", 0) == "hello"

    def test_bold(self):
        assert _wrap_format("x", _FORMAT_BOLD) == "<strong>x</strong>"

    def test_italic(self):
        assert _wrap_format("x", _FORMAT_ITALIC) == "<em>x</em>"

    def test_strikethrough(self):
        assert _wrap_format("x", _FORMAT_STRIKETHROUGH) == "<s>x</s>"

    def test_underline(self):
        assert _wrap_format("x", _FORMAT_UNDERLINE) == "<u>x</u>"

    def test_code(self):
        assert _wrap_format("x", _FORMAT_CODE) == "<code>x</code>"

    def test_subscript(self):
        assert _wrap_format("x", _FORMAT_SUBSCRIPT) == "<sub>x</sub>"

    def test_superscript(self):
        assert _wrap_format("x", _FORMAT_SUPERSCRIPT) == "<sup>x</sup>"

    def test_highlight(self):
        assert _wrap_format("x", _FORMAT_HIGHLIGHT) == "<mark>x</mark>"

    def test_bold_italic(self):
        result = _wrap_format("x", _FORMAT_BOLD | _FORMAT_ITALIC)
        assert "<strong>" in result
        assert "<em>" in result

    def test_all_flags(self):
        all_flags = (
            _FORMAT_BOLD | _FORMAT_ITALIC | _FORMAT_STRIKETHROUGH |
            _FORMAT_UNDERLINE | _FORMAT_CODE | _FORMAT_SUBSCRIPT |
            _FORMAT_SUPERSCRIPT | _FORMAT_HIGHLIGHT
        )
        result = _wrap_format("x", all_flags)
        for tag in ["strong", "em", "s", "u", "code", "sub", "sup", "mark"]:
            assert f"<{tag}>" in result
            assert f"</{tag}>" in result


# ---------------------------------------------------------------------------
# _align_style
# ---------------------------------------------------------------------------

class TestAlignStyle:
    def test_no_format(self):
        assert _align_style({}) == ""

    def test_format_zero(self):
        assert _align_style({"format": 0}) == ""

    def test_left(self):
        assert _align_style({"format": 1}) == ' style="text-align: left"'

    def test_center(self):
        assert _align_style({"format": 2}) == ' style="text-align: center"'

    def test_right(self):
        assert _align_style({"format": 3}) == ' style="text-align: right"'

    def test_justify(self):
        assert _align_style({"format": 4}) == ' style="text-align: justify"'

    def test_string_format(self):
        assert _align_style({"format": "center"}) == ' style="text-align: center"'

    def test_unmapped_int(self):
        assert _align_style({"format": 99}) == ""


# ---------------------------------------------------------------------------
# render_lexical — text nodes
# ---------------------------------------------------------------------------

class TestRenderLexicalText:
    def test_empty_doc(self):
        assert render_lexical({"root": {"children": []}}) == ""

    def test_plain_text(self):
        doc = {"root": {"children": [
            {"type": "text", "text": "hello"}
        ]}}
        assert render_lexical(doc) == "hello"

    def test_html_escape(self):
        doc = {"root": {"children": [
            {"type": "text", "text": "<script>alert('xss')</script>"}
        ]}}
        result = render_lexical(doc)
        assert "<script>" not in result
        assert "&lt;script&gt;" in result

    def test_bold_text(self):
        doc = {"root": {"children": [
            {"type": "text", "text": "bold", "format": _FORMAT_BOLD}
        ]}}
        assert render_lexical(doc) == "<strong>bold</strong>"

    def test_string_input(self):
        import json
        doc = {"root": {"children": [{"type": "text", "text": "hi"}]}}
        assert render_lexical(json.dumps(doc)) == "hi"


# ---------------------------------------------------------------------------
# render_lexical — block nodes
# ---------------------------------------------------------------------------

class TestRenderLexicalBlocks:
    def test_paragraph(self):
        doc = {"root": {"children": [
            {"type": "paragraph", "children": [
                {"type": "text", "text": "hello"}
            ]}
        ]}}
        assert render_lexical(doc) == "<p>hello</p>"

    def test_empty_paragraph(self):
        doc = {"root": {"children": [
            {"type": "paragraph", "children": []}
        ]}}
        assert render_lexical(doc) == "<p><br></p>"

    def test_heading_default(self):
        doc = {"root": {"children": [
            {"type": "heading", "children": [
                {"type": "text", "text": "title"}
            ]}
        ]}}
        assert render_lexical(doc) == "<h2>title</h2>"

    def test_heading_h3(self):
        doc = {"root": {"children": [
            {"type": "heading", "tag": "h3", "children": [
                {"type": "text", "text": "title"}
            ]}
        ]}}
        assert render_lexical(doc) == "<h3>title</h3>"

    def test_blockquote(self):
        doc = {"root": {"children": [
            {"type": "quote", "children": [
                {"type": "text", "text": "quoted"}
            ]}
        ]}}
        assert render_lexical(doc) == "<blockquote>quoted</blockquote>"

    def test_linebreak(self):
        doc = {"root": {"children": [{"type": "linebreak"}]}}
        assert render_lexical(doc) == "<br>"

    def test_horizontal_rule(self):
        doc = {"root": {"children": [{"type": "horizontalrule"}]}}
        assert render_lexical(doc) == "<hr>"

    def test_unordered_list(self):
        doc = {"root": {"children": [
            {"type": "list", "listType": "bullet", "children": [
                {"type": "listitem", "children": [
                    {"type": "text", "text": "item"}
                ]}
            ]}
        ]}}
        assert render_lexical(doc) == "<ul><li>item</li></ul>"

    def test_ordered_list(self):
        doc = {"root": {"children": [
            {"type": "list", "listType": "number", "children": [
                {"type": "listitem", "children": [
                    {"type": "text", "text": "one"}
                ]}
            ]}
        ]}}
        assert render_lexical(doc) == "<ol><li>one</li></ol>"

    def test_ordered_list_custom_start(self):
        doc = {"root": {"children": [
            {"type": "list", "listType": "number", "start": 5, "children": []}
        ]}}
        result = render_lexical(doc)
        assert 'start="5"' in result

    def test_link(self):
        doc = {"root": {"children": [
            {"type": "link", "url": "https://example.com", "children": [
                {"type": "text", "text": "click"}
            ]}
        ]}}
        result = render_lexical(doc)
        assert 'href="https://example.com"' in result
        assert "click" in result

    def test_link_xss_url(self):
        doc = {"root": {"children": [
            {"type": "link", "url": 'javascript:alert("xss")', "children": []}
        ]}}
        result = render_lexical(doc)
        assert "javascript:alert(&quot;xss&quot;)" in result


# ---------------------------------------------------------------------------
# render_lexical — cards
# ---------------------------------------------------------------------------

class TestRenderLexicalCards:
    def test_image(self):
        doc = {"root": {"children": [
            {"type": "image", "src": "photo.jpg", "alt": "A photo"}
        ]}}
        result = render_lexical(doc)
        assert "kg-image-card" in result
        assert 'src="photo.jpg"' in result
        assert 'alt="A photo"' in result
        assert 'loading="lazy"' in result

    def test_image_wide(self):
        doc = {"root": {"children": [
            {"type": "image", "src": "x.jpg", "cardWidth": "wide"}
        ]}}
        assert "kg-width-wide" in render_lexical(doc)

    def test_image_with_caption(self):
        doc = {"root": {"children": [
            {"type": "image", "src": "x.jpg", "caption": "Caption text"}
        ]}}
        assert "<figcaption>Caption text</figcaption>" in render_lexical(doc)

    def test_codeblock(self):
        doc = {"root": {"children": [
            {"type": "codeblock", "code": "print('hi')", "language": "python"}
        ]}}
        result = render_lexical(doc)
        assert 'class="language-python"' in result
        assert "print(&#x27;hi&#x27;)" in result

    def test_html_card(self):
        doc = {"root": {"children": [
            {"type": "html", "html": "<div>raw</div>"}
        ]}}
        result = render_lexical(doc)
        assert "<!--kg-card-begin: html-->" in result
        assert "<div>raw</div>" in result

    def test_markdown_card(self):
        doc = {"root": {"children": [
            {"type": "markdown", "markdown": "**bold**"}
        ]}}
        result = render_lexical(doc)
        assert "<!--kg-card-begin: markdown-->" in result
        assert "<strong>bold</strong>" in result

    def test_callout(self):
        doc = {"root": {"children": [
            {"type": "callout", "backgroundColor": "blue",
             "calloutEmoji": "💡", "children": [
                {"type": "text", "text": "Note"}
            ]}
        ]}}
        result = render_lexical(doc)
        assert "kg-callout-card-blue" in result
        assert "💡" in result

    def test_button(self):
        doc = {"root": {"children": [
            {"type": "button", "buttonText": "Click",
             "buttonUrl": "https://example.com", "alignment": "left"}
        ]}}
        result = render_lexical(doc)
        assert "kg-align-left" in result
        assert "Click" in result

    def test_toggle(self):
        doc = {"root": {"children": [
            {"type": "toggle", "heading": "FAQ", "children": [
                {"type": "text", "text": "Answer"}
            ]}
        ]}}
        result = render_lexical(doc)
        assert "kg-toggle-card" in result
        assert "FAQ" in result

    def test_audio_duration(self):
        doc = {"root": {"children": [
            {"type": "audio", "src": "a.mp3", "title": "Song", "duration": 185}
        ]}}
        result = render_lexical(doc)
        assert "3:05" in result

    def test_audio_zero_duration(self):
        doc = {"root": {"children": [
            {"type": "audio", "src": "a.mp3", "duration": 0}
        ]}}
        assert "0:00" in render_lexical(doc)

    def test_video(self):
        doc = {"root": {"children": [
            {"type": "video", "src": "v.mp4", "loop": True}
        ]}}
        result = render_lexical(doc)
        assert "kg-video-card" in result
        assert " loop" in result

    def test_file_size_kb(self):
        doc = {"root": {"children": [
            {"type": "file", "src": "f.pdf", "fileName": "doc.pdf",
             "fileSize": 512000}  # 500 KB
        ]}}
        assert "500 KB" in render_lexical(doc)

    def test_file_size_mb(self):
        doc = {"root": {"children": [
            {"type": "file", "src": "f.zip", "fileName": "big.zip",
             "fileSize": 5242880}  # 5 MB
        ]}}
        assert "5.0 MB" in render_lexical(doc)

    def test_paywall(self):
        doc = {"root": {"children": [{"type": "paywall"}]}}
        assert render_lexical(doc) == "<!--members-only-->"

    def test_embed(self):
        doc = {"root": {"children": [
            {"type": "embed", "html": "<iframe></iframe>",
             "caption": "Video"}
        ]}}
        result = render_lexical(doc)
        assert "kg-embed-card" in result
        assert "<figcaption>Video</figcaption>" in result

    def test_bookmark(self):
        doc = {"root": {"children": [
            {"type": "bookmark", "url": "https://example.com",
             "metadata": {"title": "Example", "description": "A site"}}
        ]}}
        result = render_lexical(doc)
        assert "kg-bookmark-card" in result
        assert "Example" in result

    def test_unknown_node_ignored(self):
        doc = {"root": {"children": [
            {"type": "unknown-future-thing"}
        ]}}
        assert render_lexical(doc) == ""

    def test_product_stars(self):
        doc = {"root": {"children": [
            {"type": "product", "productTitle": "Widget",
             "rating": 3, "productDescription": "Nice"}
        ]}}
        result = render_lexical(doc)
        assert "kg-product-card" in result
        assert result.count("kg-product-card-rating-active") == 3

    def test_header_card(self):
        doc = {"root": {"children": [
            {"type": "header", "heading": "Welcome",
             "size": "large", "style": "dark"}
        ]}}
        result = render_lexical(doc)
        assert "kg-header-card" in result
        assert "kg-size-large" in result
        assert "Welcome" in result

    def test_signup_card(self):
        doc = {"root": {"children": [
            {"type": "signup", "heading": "Subscribe",
             "buttonText": "Join", "style": "light"}
        ]}}
        result = render_lexical(doc)
        assert "kg-signup-card" in result
        assert "Join" in result
