"""The editorial Markdown renderer (app/services/markdown.py).

The article body is rendered once, server-side, and inserted as HTML by the SPA and by the
crawler-facing page alike, so the renderer is the whole XSS surface for text a client
hands in through Word files nobody vets. Every case here is a rule the docstring states.
"""

from app.services.markdown import heading_id, plain_text, render_inline, render_markdown


class TestSafety:
    def test_raw_html_is_text(self):
        out = render_markdown("<script>alert(1)</script> and <img src=x onerror=alert(1)>")
        assert "<script" not in out and "<img" not in out
        assert "&lt;script&gt;" in out

    def test_javascript_links_render_as_text(self):
        out = render_inline(
            "[click](javascript:alert(1)) and ![x](javascript:alert(1)) and [d](data:text/html,x)"
        )
        assert "href" not in out and "src" not in out
        assert "click" in out and "x" in out and "d" in out

    def test_safe_links_and_images(self):
        assert '<a href="https://a.example/p" rel="noopener">shop</a>' in render_inline(
            "[shop](https://a.example/p)"
        )
        assert '<a href="/products/x-1">bottle</a>' in render_inline("[bottle](/products/x-1)")
        assert '<a href="mailto:hi@example.com">' in render_inline("[mail](mailto:hi@example.com)")
        assert "//evil.example" not in render_inline("[p](//evil.example/x)").replace("p", "")
        assert '<img src="https://a.example/i.png" alt="An &quot;alt&quot;"' in render_inline(
            '![An "alt"](https://a.example/i.png)'
        )

    def test_attribute_values_are_escaped(self):
        out = render_inline('[t](https://a.example/?q="><script>)')
        assert "<script" not in out and "&quot;&gt;&lt;script&gt;" in out

    def test_placeholder_image_from_a_word_file_is_left_out(self):
        assert render_markdown("![image 1](TODO-image-1)") == ""
        assert "<img" not in render_markdown("Text ![alt](TODO-image-2) more")


class TestBlocks:
    def test_title_level_heading_becomes_h2_and_deeper_caps_at_h4(self):
        out = render_markdown("# Top\n\n## Two\n\n### Three\n\n##### Five")
        assert '<h2 id="top">Top</h2>' in out and "<h2" in out and "<h3" in out
        assert "<h5" not in out and out.count("<h4") == 1

    def test_paragraphs_lists_quote_rule_and_fence(self):
        md = "First para\nsame para\n\n- a\n- b `c`\n\n1. one\n2. two\n\n> quoted\n> text\n\n---\n\n```\nx < y\n```"
        out = render_markdown(md)
        assert "<p>First para same para</p>" in out
        assert "<ul><li>a</li><li>b <code>c</code></li></ul>" in out
        assert "<ol><li>one</li><li>two</li></ol>" in out
        assert "<blockquote><p>quoted text</p></blockquote>" in out
        assert "<hr />" in out
        assert "<pre><code>x &lt; y</code></pre>" in out

    def test_nested_list_and_continuation_line(self):
        out = render_markdown("- a\n  still a\n    1. inner\n- b")
        assert out == "<ul><li>a still a<ol><li>inner</li></ol></li><li>b</li></ul>"

    def test_hard_break_and_figure(self):
        assert "line one<br />line two" in render_markdown("line one  \nline two")
        out = render_markdown("![Caption here](https://a.example/i.jpg)")
        assert out.startswith("<figure><img") and "<figcaption>Caption here</figcaption>" in out

    def test_empty_and_whitespace(self):
        assert render_markdown("") == "" and render_markdown("\n\n  \n") == ""


class TestInline:
    def test_emphasis_forms_including_mammoth_style_bold(self):
        assert render_inline("__b__ **b** *i* _i_ ***bi***") == (
            "<strong>b</strong> <strong>b</strong> <em>i</em> <em>i</em> <strong><em>bi</em></strong>"
        )

    def test_underscores_inside_words_are_text(self):
        assert render_inline("snake_case_name and wp_posts") == "snake_case_name and wp_posts"

    def test_unclosed_markers_are_text(self):
        assert render_inline("a * b and 2 * 3 and _x") == "a * b and 2 * 3 and _x"

    def test_backslash_escapes_from_mammoth(self):
        assert (
            render_inline("release\\. Price\\-match \\*not bold\\* \\#1")
            == "release. Price-match *not bold* #1"
        )

    def test_autolink_stops_at_punctuation(self):
        out = render_inline("See https://a.example/x). Then")
        assert '<a href="https://a.example/x" rel="noopener">https://a.example/x</a>).' in out

    def test_link_with_parentheses_in_the_address(self):
        out = render_inline("[w](https://en.wikipedia.org/wiki/Whisky_(Scotch)) after")
        assert 'href="https://en.wikipedia.org/wiki/Whisky_(Scotch)"' in out and out.endswith(
            "after"
        )


class TestText:
    def test_plain_text_and_excerpt(self):
        md = "# T\n\nThe **first** words of a long body that keeps going and going.\n\n- item"
        assert plain_text(md) == "T The first words of a long body that keeps going and going. item"
        assert plain_text(md, 24) == "T The first words of a..."

    def test_heading_id(self):
        assert heading_id("Heathrow: Terminal 5 & beyond!") == "heathrow-terminal-5-beyond"
