"""Tests for the Jinja ↔ s-expression bridge."""

import asyncio

from shared.sx.jinja_bridge import (
    sx,
    sx_async,
    register_components,
    get_component_env,
    _COMPONENT_ENV,
)


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

def run(coro):
    return asyncio.run(coro)


def setup_function():
    """Clear component env before each test."""
    _COMPONENT_ENV.clear()


# ---------------------------------------------------------------------------
# sx() — synchronous rendering
# ---------------------------------------------------------------------------

class TestSx:
    def test_simple_html(self):
        assert sx('(div "Hello")') == "<div>Hello</div>"

    def test_with_kwargs(self):
        html = sx('(p name)', name="Alice")
        assert html == "<p>Alice</p>"

    def test_multiple_kwargs(self):
        html = sx('(a :href url title)', url="/about", title="About")
        assert html == '<a href="/about">About</a>'

    def test_escaping(self):
        html = sx('(p text)', text="<script>alert(1)</script>")
        assert "&lt;script&gt;" in html
        assert "<script>" not in html

    def test_nested(self):
        html = sx('(div :class "card" (h1 title))', title="Hi")
        assert html == '<div class="card"><h1>Hi</h1></div>'


# ---------------------------------------------------------------------------
# register_components() + sx()
# ---------------------------------------------------------------------------

class TestComponents:
    def test_register_and_use(self):
        register_components('''
            (defcomp ~shared:misc/badge (&key label)
              (span :class "badge" label))
        ''')
        html = sx('(~shared:misc/badge :label "New")')
        assert html == '<span class="badge">New</span>'

    def test_multiple_components(self):
        register_components('''
            (defcomp ~tag (&key text)
              (span :class "tag" text))
            (defcomp ~pill (&key text)
              (span :class "pill" text))
        ''')
        assert '<span class="tag">A</span>' == sx('(~tag :text "A")')
        assert '<span class="pill">B</span>' == sx('(~pill :text "B")')

    def test_component_with_children(self):
        register_components('''
            (defcomp ~box (&key title &rest children)
              (div :class "box" (h2 title) children))
        ''')
        html = sx('(~box :title "Box" (p "Content"))')
        assert '<div class="box">' in html
        assert "<h2>Box</h2>" in html
        assert "<p>Content</p>" in html

    def test_component_with_kwargs_override(self):
        """Kwargs passed to sx() are available alongside components."""
        register_components('''
            (defcomp ~greeting (&key name)
              (p (str "Hello " name)))
        ''')
        html = sx('(~greeting :name user)', user="Bob")
        assert html == "<p>Hello Bob</p>"

    def test_component_env_persists(self):
        """Components registered once are available in subsequent calls."""
        register_components('(defcomp ~x (&key v) (b v))')
        assert sx('(~x :v "1")') == "<b>1</b>"
        assert sx('(~x :v "2")') == "<b>2</b>"

    def test_get_component_env(self):
        register_components('(defcomp ~foo (&key x) (span x))')
        env = get_component_env()
        assert "~foo" in env


# ---------------------------------------------------------------------------
# Link card example — the first migration target
# ---------------------------------------------------------------------------

class TestLinkCard:
    def setup_method(self):
        _COMPONENT_ENV.clear()
        register_components('''
            (defcomp ~shared:fragments/link-card (&key link title image icon brand)
              (a :href link
                 :class "block rounded border border-stone-200 bg-white hover:bg-stone-50 transition-colors no-underline"
                (div :class "flex flex-row items-start gap-3 p-3"
                  (if image
                    (img :src image :alt "" :class "flex-shrink-0 w-16 h-16 rounded object-cover")
                    (div :class "flex-shrink-0 w-16 h-16 rounded bg-stone-100 flex items-center justify-center text-stone-400"
                      (i :class icon)))
                  (div :class "flex-1 min-w-0"
                    (div :class "font-medium text-stone-900 text-sm clamp-2" title)
                    (when brand
                      (div :class "text-xs text-stone-500 mt-0.5" brand))))))
        ''')

    def test_with_image(self):
        html = sx('''
            (~shared:fragments/link-card
              :link "/products/apple/"
              :title "Apple"
              :image "/img/apple.jpg"
              :icon "fas fa-shopping-bag")
        ''')
        assert 'href="/products/apple/"' in html
        assert '<img src="/img/apple.jpg"' in html
        assert "Apple" in html

    def test_without_image(self):
        html = sx('''
            (~shared:fragments/link-card
              :link "/posts/hello/"
              :title "Hello World"
              :icon "fas fa-file-alt")
        ''')
        assert 'href="/posts/hello/"' in html
        assert "<img" not in html
        assert "fas fa-file-alt" in html
        assert "Hello World" in html

    def test_with_brand(self):
        html = sx('''
            (~shared:fragments/link-card
              :link "/p/x/"
              :title "Widget"
              :image "/img/w.jpg"
              :brand "Acme Corp")
        ''')
        assert "Acme Corp" in html

    def test_without_brand(self):
        html = sx('''
            (~shared:fragments/link-card
              :link "/p/x/"
              :title "Widget"
              :image "/img/w.jpg")
        ''')
        # brand div should not appear
        assert "mt-0.5" not in html

    def test_kwargs_from_python(self):
        """Pass data from Python (like a route handler would)."""
        html = sx(
            '(~shared:fragments/link-card :link link :title title :image image :icon "fas fa-box")',
            link="/products/banana/",
            title="Banana",
            image="/img/banana.jpg",
        )
        assert 'href="/products/banana/"' in html
        assert "Banana" in html


# ---------------------------------------------------------------------------
# sx_async() — async rendering (no real I/O, just passthrough)
# ---------------------------------------------------------------------------

class TestSxAsync:
    def test_simple(self):
        html = run(sx_async('(div "Async")'))
        assert html == "<div>Async</div>"

    def test_with_kwargs(self):
        html = run(sx_async('(p name)', name="Alice"))
        assert html == "<p>Alice</p>"

    def test_with_component(self):
        register_components('(defcomp ~x (&key v) (b v))')
        html = run(sx_async('(~x :v "OK")'))
        assert html == "<b>OK</b>"
