"""Unit tests for the Lexical JSON → sx converter."""
from __future__ import annotations

import sys
import os
import pytest

# Add project root so shared.sx.html_to_sx resolves, plus the ghost dir.
_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
sys.path.insert(0, _project_root)
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "bp", "blog", "ghost"))
from lexical_to_sx import lexical_to_sx


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _doc(*children):
    """Wrap children in a minimal Lexical document."""
    return {"root": {"children": list(children)}}


def _text(s, fmt=0):
    return {"type": "text", "text": s, "format": fmt}


def _paragraph(*children):
    return {"type": "paragraph", "children": list(children)}


def _heading(tag, *children):
    return {"type": "heading", "tag": tag, "children": list(children)}


def _link(url, *children):
    return {"type": "link", "url": url, "children": list(children)}


# ---------------------------------------------------------------------------
# Basic text
# ---------------------------------------------------------------------------

class TestBasicText:
    def test_empty_doc(self):
        result = lexical_to_sx(_doc())
        assert result == '(<> (p ""))'

    def test_single_paragraph(self):
        result = lexical_to_sx(_doc(_paragraph(_text("Hello"))))
        assert result == '(p "Hello")'

    def test_two_paragraphs(self):
        result = lexical_to_sx(_doc(
            _paragraph(_text("Hello")),
            _paragraph(_text("World")),
        ))
        assert "(p " in result
        assert '"Hello"' in result
        assert '"World"' in result

    def test_heading(self):
        result = lexical_to_sx(_doc(_heading("h2", _text("Title"))))
        assert result == '(h2 "Title")'

    def test_h3(self):
        result = lexical_to_sx(_doc(_heading("h3", _text("Sub"))))
        assert result == '(h3 "Sub")'


# ---------------------------------------------------------------------------
# Formatting
# ---------------------------------------------------------------------------

class TestFormatting:
    def test_bold(self):
        result = lexical_to_sx(_doc(_paragraph(_text("hi", 1))))
        assert "(strong " in result

    def test_italic(self):
        result = lexical_to_sx(_doc(_paragraph(_text("hi", 2))))
        assert "(em " in result

    def test_strikethrough(self):
        result = lexical_to_sx(_doc(_paragraph(_text("hi", 4))))
        assert "(s " in result

    def test_bold_italic(self):
        result = lexical_to_sx(_doc(_paragraph(_text("hi", 3))))
        assert "(strong " in result
        assert "(em " in result

    def test_code(self):
        result = lexical_to_sx(_doc(_paragraph(_text("x", 16))))
        assert "(code " in result


# ---------------------------------------------------------------------------
# Links
# ---------------------------------------------------------------------------

class TestLinks:
    def test_link(self):
        result = lexical_to_sx(_doc(
            _paragraph(_link("https://example.com", _text("click")))
        ))
        assert '(a :href "https://example.com"' in result
        assert '"click"' in result


# ---------------------------------------------------------------------------
# Lists
# ---------------------------------------------------------------------------

class TestLists:
    def test_unordered_list(self):
        result = lexical_to_sx(_doc({
            "type": "list", "listType": "bullet",
            "children": [
                {"type": "listitem", "children": [_text("one")]},
                {"type": "listitem", "children": [_text("two")]},
            ]
        }))
        assert "(ul " in result
        assert "(li " in result
        assert '"one"' in result

    def test_ordered_list(self):
        result = lexical_to_sx(_doc({
            "type": "list", "listType": "number",
            "children": [
                {"type": "listitem", "children": [_text("first")]},
            ]
        }))
        assert "(ol " in result


# ---------------------------------------------------------------------------
# Block elements
# ---------------------------------------------------------------------------

class TestBlocks:
    def test_hr(self):
        result = lexical_to_sx(_doc({"type": "horizontalrule"}))
        assert result == "(hr)"

    def test_quote(self):
        result = lexical_to_sx(_doc({
            "type": "quote", "children": [_text("wisdom")]
        }))
        assert '(blockquote "wisdom")' == result

    def test_codeblock(self):
        result = lexical_to_sx(_doc({
            "type": "codeblock", "code": "print('hi')", "language": "python"
        }))
        assert '(pre (code :class "language-python"' in result
        assert "print" in result


# ---------------------------------------------------------------------------
# Cards
# ---------------------------------------------------------------------------

class TestCards:
    def test_image(self):
        result = lexical_to_sx(_doc({
            "type": "image", "src": "photo.jpg", "alt": "test"
        }))
        assert '(~kg-image :src "photo.jpg" :alt "test")' == result

    def test_image_wide_with_caption(self):
        result = lexical_to_sx(_doc({
            "type": "image", "src": "p.jpg", "alt": "",
            "cardWidth": "wide", "caption": "Fig 1"
        }))
        assert ':width "wide"' in result
        assert ':caption "Fig 1"' in result

    def test_image_html_caption(self):
        result = lexical_to_sx(_doc({
            "type": "image", "src": "p.jpg", "alt": "",
            "caption": 'Photo by <a href="https://x.com">Author</a>'
        }))
        assert ':caption (<> "Photo by " (a :href "https://x.com" "Author"))' in result

    def test_bookmark(self):
        result = lexical_to_sx(_doc({
            "type": "bookmark", "url": "https://example.com",
            "metadata": {"title": "Example", "description": "A site"}
        }))
        assert "(~kg-bookmark " in result
        assert ':url "https://example.com"' in result
        assert ':title "Example"' in result

    def test_callout(self):
        result = lexical_to_sx(_doc({
            "type": "callout", "backgroundColor": "blue",
            "calloutEmoji": "💡",
            "children": [_text("Note")]
        }))
        assert "(~kg-callout " in result
        assert ':color "blue"' in result

    def test_button(self):
        result = lexical_to_sx(_doc({
            "type": "button", "buttonText": "Click",
            "buttonUrl": "https://example.com"
        }))
        assert "(~kg-button " in result
        assert ':text "Click"' in result

    def test_toggle(self):
        result = lexical_to_sx(_doc({
            "type": "toggle", "heading": "FAQ",
            "children": [_text("Answer")]
        }))
        assert "(~kg-toggle " in result
        assert ':heading "FAQ"' in result

    def test_html(self):
        result = lexical_to_sx(_doc({
            "type": "html", "html": "<div>custom</div>"
        }))
        assert result == '(~kg-html (div "custom"))'

    def test_embed(self):
        result = lexical_to_sx(_doc({
            "type": "embed", "html": "<iframe></iframe>",
            "caption": "Video"
        }))
        assert "(~kg-embed " in result
        assert ':caption "Video"' in result

    def test_markdown(self):
        result = lexical_to_sx(_doc({
            "type": "markdown", "markdown": "**bold** text"
        }))
        assert result.startswith("(~kg-md ")
        assert "(p " in result
        assert "(strong " in result

    def test_video(self):
        result = lexical_to_sx(_doc({
            "type": "video", "src": "v.mp4", "cardWidth": "wide"
        }))
        assert "(~kg-video " in result
        assert ':width "wide"' in result

    def test_audio(self):
        result = lexical_to_sx(_doc({
            "type": "audio", "src": "s.mp3", "title": "Song", "duration": 195
        }))
        assert "(~kg-audio " in result
        assert ':duration "3:15"' in result

    def test_file(self):
        result = lexical_to_sx(_doc({
            "type": "file", "src": "f.pdf", "fileName": "doc.pdf",
            "fileSize": 2100000
        }))
        assert "(~kg-file " in result
        assert ':filename "doc.pdf"' in result
        assert "MB" in result

    def test_paywall(self):
        result = lexical_to_sx(_doc({"type": "paywall"}))
        assert result == "(~kg-paywall)"


# ---------------------------------------------------------------------------
# Escaping
# ---------------------------------------------------------------------------

class TestEscaping:
    def test_quotes_in_text(self):
        result = lexical_to_sx(_doc(_paragraph(_text('He said "hello"'))))
        assert '\\"hello\\"' in result

    def test_backslash_in_text(self):
        result = lexical_to_sx(_doc(_paragraph(_text("a\\b"))))
        assert "a\\\\b" in result


# ---------------------------------------------------------------------------
# JSON string input
# ---------------------------------------------------------------------------

class TestJsonString:
    def test_string_input(self):
        import json
        doc = _doc(_paragraph(_text("test")))
        result = lexical_to_sx(json.dumps(doc))
        assert '(p "test")' == result
