use crate::nodes::{NodeCode, NodeValue};

use super::*;

#[test]
fn basic() {
    html(
        concat!(
            "My **document**.\n",
            "\n",
            "It's mine.\n",
            "\n",
            "> Yes.\n",
            "\n",
            "## Hi!\n",
            "\n",
            "Okay.\n"
        ),
        concat!(
            "<p>My <strong>document</strong>.</p>\n",
            "<p>It's mine.</p>\n",
            "<blockquote>\n",
            "<p>Yes.</p>\n",
            "</blockquote>\n",
            "<h2>Hi!</h2>\n",
            "<p>Okay.</p>\n"
        ),
    );
}

#[test]
fn codefence() {
    html(
        concat!("``` rust yum\n", "fn main<'a>();\n", "```\n"),
        concat!(
            "<pre><code class=\"language-rust\">fn main&lt;'a&gt;();\n",
            "</code></pre>\n"
        ),
    );
}

#[test]
fn lists() {
    html(
        concat!("2. Hello.\n", "3. Hi.\n"),
        concat!(
            "<ol start=\"2\">\n",
            "<li>Hello.</li>\n",
            "<li>Hi.</li>\n",
            "</ol>\n"
        ),
    );

    html(
        concat!("- Hello.\n", "- Hi.\n"),
        concat!("<ul>\n", "<li>Hello.</li>\n", "<li>Hi.</li>\n", "</ul>\n"),
    );
}
#[test]
fn thematic_breaks() {
    html(
        concat!("---\n", "\n", "- - -\n", "\n", "\n", "_        _   _\n"),
        concat!("<hr />\n", "<hr />\n", "<hr />\n"),
    );
}

#[test]
fn atx_heading() {
    html(
        concat!("# h1\n", "foo\n", "## h2\n"),
        concat!("<h1>h1</h1>\n", "<p>foo</p>\n", "<h2>h2</h2>\n"),
    );
}

#[test]
fn atx_heading_sourcepos() {
    assert_ast_match!(
        [],
        "# h1\n"
        "foo\n"
        "## h2\n",
        (document (1:1-3:5) [
            (heading (1:1-1:4) [
                (text (1:3-1:4) "h1")
            ])
            (paragraph (2:1-2:3) [
                (text (2:1-2:3) "foo")
            ])
            (heading (3:1-3:5) [
                (text (3:4-3:5) "h2")
            ])
        ])
    );
}

#[test]
fn setext_heading() {
    html(
        concat!("Hi\n", "==\n", "\n", "Ok\n", "-----\n"),
        concat!("<h1>Hi</h1>\n", "<h2>Ok</h2>\n"),
    );
}

#[test]
fn setext_heading_sourcepos() {
    assert_ast_match!(
        [],
        "Header\n"
        "---\n"
        "this",
        (document (1:1-3:4) [
            (heading (1:1-2:3) [
                (text (1:1-1:6) "Header")
            ])
            (paragraph (3:1-3:4) [
                (text (3:1-3:4) "this")
            ])
        ])
    );
}

#[test]
fn ignore_setext_heading() {
    html_opts!(
        [render.ignore_setext],
        concat!("text text\n---"),
        concat!("<p>text text</p>\n<hr />\n"),
    );
}

#[test]
fn figure_with_caption_with_title() {
    html_opts!(
        [render.figure_with_caption],
        concat!("![image](https://example.com/image.png \"this is an image\")\n"),
        concat!("<p><figure><img src=\"https://example.com/image.png\" alt=\"image\" title=\"this is an image\" /><figcaption>this is an image</figcaption></figure></p>\n"),
    );
}

#[test]
fn figure_with_caption_without_title() {
    html_opts!(
        [render.figure_with_caption],
        concat!("![image](https://example.com/image.png)\n"),
        concat!(
            "<p><figure><img src=\"https://example.com/image.png\" alt=\"image\" /></figure></p>\n"
        ),
    );
}

#[test]
fn html_block_1() {
    html_opts!(
        [render.unsafe_],
        concat!(
            "<script>\n",
            "*ok* </script> *ok*\n",
            "\n",
            "*ok*\n",
            "\n",
            "*ok*\n",
            "\n",
            "<pre x>\n",
            "*ok*\n",
            "</style>\n",
            "*ok*\n",
            "<style>\n",
            "*ok*\n",
            "</style>\n",
            "\n",
            "*ok*\n"
        ),
        concat!(
            "<script>\n",
            "*ok* </script> *ok*\n",
            "<p><em>ok</em></p>\n",
            "<p><em>ok</em></p>\n",
            "<pre x>\n",
            "*ok*\n",
            "</style>\n",
            "<p><em>ok</em></p>\n",
            "<style>\n",
            "*ok*\n",
            "</style>\n",
            "<p><em>ok</em></p>\n"
        ),
    );
}

#[test]
fn html_block_2() {
    html_opts!(
        [render.unsafe_],
        concat!("   <!-- abc\n", "\n", "ok --> *hi*\n", "*hi*\n"),
        concat!(
            "   <!-- abc\n",
            "\n",
            "ok --> *hi*\n",
            "<p><em>hi</em></p>\n"
        ),
    );
}

#[test]
fn html_block_3() {
    html_opts!(
        [render.unsafe_],
        concat!(" <? o\n", "k ?> *a*\n", "*a*\n"),
        concat!(" <? o\n", "k ?> *a*\n", "<p><em>a</em></p>\n"),
    );
}

#[test]
fn html_block_4() {
    html_opts!(
        [render.unsafe_],
        concat!("<!X >\n", "ok\n", "<!X\n", "um > h\n", "ok\n"),
        concat!("<!X >\n", "<p>ok</p>\n", "<!X\n", "um > h\n", "<p>ok</p>\n"),
    );
}

#[test]
fn html_block_5() {
    html_opts!(
        [render.unsafe_],
        concat!(
            "<![CDATA[\n",
            "\n",
            "hm >\n",
            "*ok*\n",
            "]]> *ok*\n",
            "*ok*\n"
        ),
        concat!(
            "<![CDATA[\n",
            "\n",
            "hm >\n",
            "*ok*\n",
            "]]> *ok*\n",
            "<p><em>ok</em></p>\n"
        ),
    );
}

#[test]
fn html_block_6() {
    html_opts!(
        [render.unsafe_],
        concat!(" </table>\n", "*x*\n", "\n", "ok\n", "\n", "<li\n", "*x*\n"),
        concat!(" </table>\n", "*x*\n", "<p>ok</p>\n", "<li\n", "*x*\n"),
    );
}

#[test]
fn html_block_7() {
    html_opts!(
        [render.unsafe_],
        concat!(
            "<a b >\n",
            "ok\n",
            "\n",
            "<a b=>\n",
            "ok\n",
            "\n",
            "<a b \n",
            "<a b> c\n",
            "ok\n"
        ),
        concat!(
            "<a b >\n",
            "ok\n",
            "<p>&lt;a b=&gt;\n",
            "ok</p>\n",
            "<p>&lt;a b\n",
            "<a b> c\n",
            "ok</p>\n"
        ),
    );

    html_opts!(
        [render.unsafe_],
        concat!("<a b c=x d='y' z=\"f\" >\n", "ok\n", "\n", "ok\n"),
        concat!("<a b c=x d='y' z=\"f\" >\n", "ok\n", "<p>ok</p>\n"),
    );
}

#[test]
fn backticks() {
    html(
        "Some `code\\` yep.\n",
        "<p>Some <code>code\\</code> yep.</p>\n",
    );
}

#[test]
fn backticks_empty_with_newline_should_be_space() {
    html("`\n`", "<p><code> </code></p>\n");
}

#[test]
fn blockquote_hard_linebreak_space() {
    html(">\\\n A", "<blockquote>\n<p><br />\nA</p>\n</blockquote>\n");
}

#[test]
fn blockquote_hard_linebreak_nonlazy_space() {
    html(
        "> A\\\n> B",
        "<blockquote>\n<p>A<br />\nB</p>\n</blockquote>\n",
    );
}

#[test]
fn backticks_num() {
    let input = "Some `code1`. More ``` code2 ```.\n";

    let arena = Arena::new();
    let options = Options::default();
    let root = parse_document(&arena, input, &options);

    let code1 = NodeValue::Code(NodeCode {
        num_backticks: 1,
        literal: "code1".to_string(),
    });
    asssert_node_eq(root, &[0, 1], &code1);

    let code2 = NodeValue::Code(NodeCode {
        num_backticks: 3,
        literal: "code2".to_string(),
    });
    asssert_node_eq(root, &[0, 3], &code2);
}

#[test]
fn backslashes() {
    html(
        concat!(
            "Some \\`fake code\\`.\n",
            "\n",
            "Some fake linebreaks:\\\n",
            "Yes.\\\n",
            "See?\n",
            "\n",
            "Ga\\rbage.\n"
        ),
        concat!(
            "<p>Some `fake code`.</p>\n",
            "<p>Some fake linebreaks:<br />\n",
            "Yes.<br />\n",
            "See?</p>\n",
            "<p>Ga\\rbage.</p>\n"
        ),
    );
}

#[test]
fn entities() {
    html(
        concat!(
            "This is &amp;, &copy;, &trade;, \\&trade;, &xyz;, &NotEqualTilde;.\n",
            "\n",
            "&#8734; &#x221e;\n"
        ),
        concat!(
            "<p>This is &amp;, ©, ™, &amp;trade;, &amp;xyz;, \u{2242}\u{338}.</p>\n",
            "<p>∞ ∞</p>\n"
        ),
    );
}

#[test]
fn links() {
    html(
        concat!(
            "Where are you [going](https://microsoft.com (today))?\n",
            "\n",
            "[Where am I?](/here)\n"
        ),
        concat!(
            "<p>Where are you <a href=\"https://microsoft.com\" \
             title=\"today\">going</a>?</p>\n",
            "<p><a href=\"/here\">Where am I?</a></p>\n"
        ),
    );
    html(
        concat!(
            r"Where are you [going](#1\.-link (today))?",
            "\n",
            "\n",
            "[Where am I?](/here)\n"
        ),
        concat!(
            "<p>Where are you <a href=\"#1.-link\" \
             title=\"today\">going</a>?</p>\n",
            "<p><a href=\"/here\">Where am I?</a></p>\n"
        ),
    );
    html(
        r"[Link Text](\\\\)",
        concat!(r##"<p><a href="%5C%5C">Link Text</a></p>"##, '\n'),
    );
    html(
        r"[Link Text](\\\\\\\\\\)",
        concat!(r##"<p><a href="%5C%5C%5C%5C%5C">Link Text</a></p>"##, '\n'),
    );
    html(
        r"[Link Text](\\\\ (title))",
        concat!(
            r##"<p><a href="%5C%5C" title="title">Link Text</a></p>"##,
            '\n'
        ),
    );
    html(
        r"[Link Text](\#)",
        concat!(r##"<p><a href="#">Link Text</a></p>"##, '\n'),
    );
}

#[test]
fn images() {
    html(
        concat!("I am ![eating [things](/url)](http://i.imgur.com/QqK1vq7.png).\n"),
        concat!(
            "<p>I am <img src=\"http://i.imgur.com/QqK1vq7.png\" alt=\"eating things\" \
             />.</p>\n"
        ),
    );
}

#[test]
fn reference_links() {
    html(
        concat!(
            "This [is] [legit], [very][honestly] legit.\n",
            "\n",
            "[legit]: ok\n",
            "[honestly]: sure \"hm\"\n"
        ),
        concat!(
            "<p>This [is] <a href=\"ok\">legit</a>, <a href=\"sure\" title=\"hm\">very</a> \
             legit.</p>\n"
        ),
    );
}

#[test]
fn reference_links_casefold() {
    html(
        concat!("[ẞ]\n", "\n", "[SS]: /url	\n",),
        "<p><a href=\"/url\">ẞ</a></p>\n",
    );
}

#[test]
fn safety() {
    html(
        concat!(
            "[data:image/png](data:image/png/x)\n\n",
            "[data:image/gif](data:image/gif/x)\n\n",
            "[data:image/jpeg](data:image/jpeg/x)\n\n",
            "[data:image/webp](data:image/webp/x)\n\n",
            "[data:malicious](data:malicious/x)\n\n",
            "[javascript:malicious](javascript:malicious)\n\n",
            "[vbscript:malicious](vbscript:malicious)\n\n",
            "[file:malicious](file:malicious)\n\n",
        ),
        concat!(
            "<p><a href=\"data:image/png/x\">data:image/png</a></p>\n",
            "<p><a href=\"data:image/gif/x\">data:image/gif</a></p>\n",
            "<p><a href=\"data:image/jpeg/x\">data:image/jpeg</a></p>\n",
            "<p><a href=\"data:image/webp/x\">data:image/webp</a></p>\n",
            "<p><a href=\"\">data:malicious</a></p>\n",
            "<p><a href=\"\">javascript:malicious</a></p>\n",
            "<p><a href=\"\">vbscript:malicious</a></p>\n",
            "<p><a href=\"\">file:malicious</a></p>\n",
        ),
    )
}

#[test]
fn link_backslash_requires_punct() {
    // Test should probably be in the spec.
    html("[a](\\ b)", "<p>[a](\\ b)</p>\n");
}

#[test]
fn nul_replacement_1() {
    html("a\0b", "<p>a\u{fffd}b</p>\n");
}

#[test]
fn nul_replacement_2() {
    html("a\0b\0c", "<p>a\u{fffd}b\u{fffd}c</p>\n");
}

#[test]
fn nul_replacement_3() {
    html("a\0\nb", "<p>a\u{fffd}\nb</p>\n");
}

#[test]
fn nul_replacement_4() {
    html("a\0\r\nb", "<p>a\u{fffd}\nb</p>\n");
}

#[test]
fn nul_replacement_5() {
    html("a\r\n\0b", "<p>a\n\u{fffd}b</p>\n");
}

#[test]
fn case_insensitive_safety() {
    html(
        "[a](javascript:a) [b](Javascript:b) [c](jaVascript:c) [d](data:xyz) [e](Data:xyz) [f](vbscripT:f) [g](FILE:g)\n",
        "<p><a href=\"\">a</a> <a href=\"\">b</a> <a href=\"\">c</a> <a href=\"\">d</a> <a href=\"\">e</a> <a href=\"\">f</a> <a href=\"\">g</a></p>\n",
    );
}

#[test]
fn link_sourcepos_baseline() {
    assert_ast_match!(
        [],
        "[ABCD](/)\n",
        (document (1:1-1:9) [
            (paragraph (1:1-1:9) [
                (link (1:1-1:9) [
                    (text (1:2-1:5) "ABCD")
                ])
            ])
        ])
    );
}

// https://github.com/kivikakk/comrak/issues/301
#[test]
fn link_sourcepos_newline() {
    assert_ast_match!(
        [],
        "[AB\nCD](/)\n",
        (document (1:1-2:6) [
            (paragraph (1:1-2:6) [
                (link (1:1-2:6) [
                    (text (1:2-1:3) "AB")
                    (softbreak (1:4-1:4))
                    (text (2:1-2:2) "CD")
                ])
            ])
        ])
    );
}

#[test]
fn link_sourcepos_truffle() {
    assert_ast_match!(
        [],
        "- A\n[![B](/B.png)](/B)\n",
        (document (1:1-2:18) [
            (list (1:1-2:18) [
                (item (1:1-2:18) [
                    (paragraph (1:3-2:18) [
                        (text (1:3-1:3) "A")
                        (softbreak (1:4-1:4))
                        (link (2:1-2:18) [
                            (image (2:2-2:13) [
                                (text (2:4-2:4) "B")
                            ])
                        ])
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn link_sourcepos_truffle_twist() {
    assert_ast_match!(
        [],
        "- A\n  [![B](/B.png)](/B)\n",
        (document (1:1-2:20) [
            (list (1:1-2:20) [
                (item (1:1-2:20) [
                    (paragraph (1:3-2:20) [
                        (text (1:3-1:3) "A")
                        (softbreak (1:4-1:4))
                        (link (2:3-2:20) [
                            (image (2:4-2:15) [
                                (text (2:6-2:6) "B")
                            ])
                        ])
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn link_sourcepos_truffle_bergamot() {
    assert_ast_match!(
        [],
        "- A\n   [![B](/B.png)](/B)\n",
        (document (1:1-2:21) [
            (list (1:1-2:21) [
                (item (1:1-2:21) [
                    (paragraph (1:3-2:21) [
                        (text (1:3-1:3) "A")
                        (softbreak (1:4-1:4))
                        (link (2:4-2:21) [
                            (image (2:5-2:16) [
                                (text (2:7-2:7) "B")
                            ])
                        ])
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn paragraph_sourcepos_multiline() {
    assert_ast_match!(
        [],
        "  A\n"
        "   B\n",
        (document (1:1-2:4) [
            (paragraph (1:3-2:4) [
                (text (1:3-1:3) "A")
                (softbreak (1:4-1:4))
                (text (2:4-2:4) "B")
            ])
        ])
    );
}

#[test]
fn listitem_sourcepos_multiline() {
    assert_ast_match!(
        [],
        "- A\n"
        "B\n",
        (document (1:1-2:1) [
            (list (1:1-2:1) [
                (item (1:1-2:1) [
                    (paragraph (1:3-2:1) [
                        (text (1:3-1:3) "A")
                        (softbreak (1:4-1:4))
                        (text (2:1-2:1) "B")
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn listitem_sourcepos_multiline_2() {
    assert_ast_match!(
        [],
        "- A\n"
        "   B\n"
        "-  C\n"
        " D",
        (document (1:1-4:2) [
            (list (1:1-4:2) [
                (item (1:1-2:4) [
                    (paragraph (1:3-2:4) [
                        (text (1:3-1:3) "A")
                        (softbreak (1:4-1:4))
                        (text (2:4-2:4) "B")
                    ])
                ])
                (item (3:1-4:2) [
                    (paragraph (3:4-4:2) [
                        (text (3:4-3:4) "C")
                        (softbreak (3:5-3:5))
                        (text (4:2-4:2) "D")
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn emphasis_sourcepos_double_1() {
    assert_ast_match!(
        [],
        "_**this**_\n",
        (document (1:1-1:10) [
            (paragraph (1:1-1:10) [
                (emph (1:1-1:10) [
                    (strong (1:2-1:9) [
                        (text (1:4-1:7) "this")
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn emphasis_sourcepos_double_2() {
    assert_ast_match!(
        [],
        "**_this_**\n",
        (document (1:1-1:10) [
            (paragraph (1:1-1:10) [
                (strong (1:1-1:10) [
                    (emph (1:3-1:8) [
                        (text (1:4-1:7) "this")
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn emphasis_sourcepos_double_3() {
    assert_ast_match!(
        [],
        "___this___\n",
        (document (1:1-1:10) [
            (paragraph (1:1-1:10) [
                (emph (1:1-1:10) [
                    (strong (1:2-1:9) [
                        (text (1:4-1:7) "this")
                    ])
                ])
            ])
        ])
    );
}

#[test]
fn emphasis_sourcepos_double_4() {
    assert_ast_match!(
        [],
        "***this***\n",
        (document (1:1-1:10) [
            (paragraph (1:1-1:10) [
                (emph (1:1-1:10) [
                    (strong (1:2-1:9) [
                        (text (1:4-1:7) "this")
                    ])
                ])
            ])
        ])
    );
}
