import unittest
from htmlnode import LeafNode
from conversions import text_node_to_html_node,split_nodes_delimiter,extract_markdown_images,extract_markdown_links,split_nodes_image,split_nodes_link,text_to_textnodes,markdown_to_blocks,block_to_block_type,markdown_to_html_node
from textnode import TextType,TextNode
import re
class TestConversions(unittest.TestCase):
    
    def test_text_node_to_html_node(self):
        text_node = TextNode("Hello, world!", TextType.NORMAL_TEXT)
        html_node = text_node_to_html_node(text_node)
        assert html_node.tag == None
        assert html_node.value == "Hello, world!"
        assert html_node.props == {}

    def test_bold_node_to_html_node(self):
        bold_node = TextNode("Hello, world!", TextType.BOLD_TEXT)
        html_node = text_node_to_html_node(bold_node)
        assert html_node.tag == "strong"
        assert html_node.value == "Hello, world!"

    def test_italic_node_to_html_node(self):
        italic_node = TextNode("Hello, world!", TextType.ITALIC_TEXT)
        html_node = text_node_to_html_node(italic_node)
        assert html_node.tag == "em"
        assert html_node.value == "Hello, world!"

    def test_code_node_to_html_node(self):
        code_node = TextNode("var hw = \"Hello, world!\"", TextType.CODE_TEXT)
        html_node = text_node_to_html_node(code_node)
        assert html_node.tag == "code"
        assert html_node.value == "var hw = \"Hello, world!\""

    def test_link_node_to_html_node(self):
        link_node = TextNode("google", TextType.LINK_TEXT,"https://google.com")
        html_node = text_node_to_html_node(link_node)
        assert html_node.tag == "a"
        assert html_node.value == "google"
        assert html_node.props == {"href":"https://google.com"}

    def test_img_node_to_html_node(self):
        img_node = TextNode("an image of an image", TextType.IMAGE_TEXT, "https://google.com")
        html_node = text_node_to_html_node(img_node)
        assert html_node.tag == "img"
        assert html_node.value == ""  # Remember, image nodes have empty value
        assert html_node.props == {
            "src": "https://google.com",
            "alt": "an image of an image"
        }

class TestSplitNodesDelimiter(unittest.TestCase):
    
    def test_basic_split(self):
        old_nodes = [TextNode("Hello *world*", TextType.NORMAL_TEXT)]
        new_nodes = split_nodes_delimiter(old_nodes, "*", TextType.ITALIC_TEXT)
        self.assertEqual(len(new_nodes), 2)
        self.assertEqual(new_nodes[0].text, "Hello ")
        self.assertEqual(new_nodes[0].text_type, TextType.NORMAL_TEXT)
        self.assertEqual(new_nodes[1].text, "world")
        self.assertEqual(new_nodes[1].text_type, TextType.ITALIC_TEXT)

    def test_unbalanced_delimiter(self):
        old_nodes = [TextNode("Hello *world", TextType.NORMAL_TEXT)]
        with self.assertRaises(Exception) as context:
            split_nodes_delimiter(old_nodes, "*", TextType.BOLD_TEXT)
        self.assertEqual(str(context.exception), "Invalid Markdown: only one delimiter")

    def test_multiple_splits(self):
        old_nodes = [TextNode("Hello *bold* and *italic* world", TextType.NORMAL_TEXT)]
        new_nodes = split_nodes_delimiter(old_nodes, "*", TextType.BOLD_TEXT)
        self.assertEqual(len(new_nodes), 5)
        self.assertEqual(new_nodes[0].text, "Hello ")
        self.assertEqual(new_nodes[1].text, "bold")
        self.assertEqual(new_nodes[1].text_type, TextType.BOLD_TEXT)
        self.assertEqual(new_nodes[2].text, " and ")
        self.assertEqual(new_nodes[3].text, "italic")
        self.assertEqual(new_nodes[3].text_type, TextType.BOLD_TEXT)
        self.assertEqual(new_nodes[4].text, " world")
        self.assertEqual(new_nodes[4].text_type, TextType.NORMAL_TEXT)

class TestExtractMarkdownImages(unittest.TestCase):
    
    def test_single_image(self):
        text = "Here is an image ![alt text](image.jpg) in markdown."
        expected = [("alt text", "image.jpg")]
        self.assertEqual(extract_markdown_images(text), expected)
    
    def test_multiple_images(self):
        text = "![first](first.jpg) and ![second](second.png)"
        expected = [("first", "first.jpg"), ("second", "second.png")]
        self.assertEqual(extract_markdown_images(text), expected)
    
    def test_no_images(self):
        text = "This is a plain text without images."
        expected = []
        self.assertEqual(extract_markdown_images(text), expected)
    
    def test_image_with_special_characters(self):
        text = "Check this out ![cool image](path/to/image-with-hyphen.jpg)"
        expected = [("cool image", "path/to/image-with-hyphen.jpg")]
        self.assertEqual(extract_markdown_images(text), expected)
    
    def test_image_with_parentheses_in_url(self):
        text = "![example](https://example.com/image(special).png)"
        expected = [("example", "https://example.com/image(special).png")]
        self.assertEqual(extract_markdown_images(text), expected)
    
    def test_image_with_no_alt_text(self):
        text = "![](no-alt.jpg)"
        expected = [("", "no-alt.jpg")]
        self.assertEqual(extract_markdown_images(text), expected)

class TestExtractMarkdownLinks(unittest.TestCase):
    
    def test_single_link(self):
        text = "This is a [link](https://example.com)."
        self.assertEqual(extract_markdown_links(text), [("link", "https://example.com")])
    
    def test_multiple_links(self):
        text = "Check [this](https://example1.com) and [that](https://example2.com)."
        self.assertEqual(extract_markdown_links(text), [("this", "https://example1.com"), ("that", "https://example2.com")])
    
    def test_no_links(self):
        text = "This is just plain text with no links."
        self.assertEqual(extract_markdown_links(text), [])
    
    def test_link_with_special_chars(self):
        text = "Click [here](https://example.com/path?query=value&other=true)."
        self.assertEqual(extract_markdown_links(text), [("here", "https://example.com/path?query=value&other=true")])
    
    def test_nested_brackets(self):
        text = "This is a [complex [link]](https://example.com)."
        self.assertEqual(extract_markdown_links(text), [("complex [link]", "https://example.com")])
    
    def test_unmatched_brackets(self):
        text = "This is a [broken link(https://example.com)."
        self.assertEqual(extract_markdown_links(text), [])
    
    def test_unmatched_parentheses(self):
        text = "This is a [broken](https://example.com link."
        self.assertEqual(extract_markdown_links(text), [])

class TestSplitNodesImage(unittest.TestCase):
    
    def test_no_images(self):
        node = TextNode("Just plain text", TextType.NORMAL_TEXT)
        assert split_nodes_image([node]) == [node]
    
    def test_one_image(self):
        node = TextNode("Text with ![image](example.png)", TextType.NORMAL_TEXT)
        nodes = split_nodes_image([node])
        assert nodes[0] == TextNode("Text with ", TextType.NORMAL_TEXT)
        assert nodes[1] == TextNode("image",TextType.IMAGE_TEXT,"example.png")
    
    def test_multiple_images(self):
        node = TextNode("Start ![one](test1.jpg) middle ![two](test2.png) end",TextType.NORMAL_TEXT)
        nodes = split_nodes_image([node])
        assert nodes[0] == TextNode("Start ",TextType.NORMAL_TEXT)   
        assert nodes[1] == TextNode("one",TextType.IMAGE_TEXT,"test1.jpg")
        assert nodes[2] == TextNode(" middle ",TextType.NORMAL_TEXT)
        assert nodes[3] == TextNode("two",TextType.IMAGE_TEXT,"test2.png")
        assert nodes[4] == TextNode(" end",TextType.NORMAL_TEXT)
    
    def test_non_text_node(self):
        node = TextNode("![image](url)", TextType.LINK_TEXT)
        nodes = split_nodes_image([node])
        assert nodes[0] == node

class TestSplitNodesLink(unittest.TestCase):
   
    def test_no_links(self):
        node = TextNode("Just plain text", TextType.NORMAL_TEXT)
        assert split_nodes_link([node]) == [node]
    
    def test_one_link(self):
        node = TextNode("Text with [link](url)", TextType.NORMAL_TEXT)
        nodes = split_nodes_link([node])
        assert nodes[0] == TextNode("Text with ",TextType.NORMAL_TEXT)
        assert nodes[1] == TextNode("link",TextType.LINK_TEXT,"url")

    def test_multiple_links(self):
        node = TextNode("Start [one](url1) middle [two](url2) end", TextType.NORMAL_TEXT)
        nodes = split_nodes_link([node])
        assert nodes[0] == TextNode("Start ",TextType.NORMAL_TEXT)
        assert nodes[1] == TextNode("one",TextType.LINK_TEXT,"url1")
        assert nodes[2] == TextNode(" middle ",TextType.NORMAL_TEXT)
        assert nodes[3] == TextNode("two",TextType.LINK_TEXT,"url2")
        assert nodes[4] == TextNode(" end",TextType.NORMAL_TEXT)

    def test_non_text_node(self):
        node = TextNode("![text](url)", TextType.IMAGE_TEXT)
        nodes = split_nodes_link([node])
        assert nodes[0] == node

class TestTextToTextNodes(unittest.TestCase):
    def test_text_to_textnodes(self):
        text = "This is a **text** *node* ![dog](../dog.jpg) and ![cat](../cat.jpg) are [dogs](http://localhost/dogs) and [cats](http://localhost/cats) this is code`codecodecodeycodecode`"
        expected_output = [
            TextNode("This is a ", TextType.NORMAL_TEXT, None),
            TextNode("text", TextType.BOLD_TEXT, None),
            TextNode("node", TextType.ITALIC_TEXT, None),
            TextNode("dog", TextType.IMAGE_TEXT, "../dog.jpg"),
            TextNode(" and ", TextType.NORMAL_TEXT, None),
            TextNode("cat", TextType.IMAGE_TEXT, "../cat.jpg"),
            TextNode(" are ", TextType.NORMAL_TEXT, None),
            TextNode("dogs", TextType.LINK_TEXT, "http://localhost/dogs"),
            TextNode(" and ", TextType.NORMAL_TEXT, None),
            TextNode("cats", TextType.LINK_TEXT, "http://localhost/cats"),
            TextNode(" this is code", TextType.NORMAL_TEXT, None),
            TextNode("codecodecodeycodecode", TextType.CODE_TEXT, None)
        ]
        self.assertEqual(text_to_textnodes(text), expected_output)

class TestMarkdownToBlocks(unittest.TestCase):
    def test_basic_paragraphs(self):
        markdown = """This is a paragraph.
        
Another paragraph."""
        expected = ["This is a paragraph.", "Another paragraph."]
        self.assertEqual(markdown_to_blocks(markdown), expected)

    def test_extra_whitespace(self):
        markdown = """  This is indented.  
        
  Another indented paragraph.  """
        expected = ["This is indented.", "Another indented paragraph."]
        self.assertEqual(markdown_to_blocks(markdown), expected)
    
    def test_empty_input(self):
        markdown = ""
        expected = []
        self.assertEqual(markdown_to_blocks(markdown), expected)

    def test_only_whitespace(self):
        markdown = "  \n  \n  "
        expected = []
        self.assertEqual(markdown_to_blocks(markdown), expected)
    
    def test_multiple_paragraphs_with_blank_lines(self):
        markdown = """First paragraph.
        
        
Second paragraph.
        

Third paragraph."""
        expected = ["First paragraph.", "Second paragraph.", "Third paragraph."]
        self.assertEqual(markdown_to_blocks(markdown), expected)
    
    def test_mixed_newline_formats(self):
        markdown = """Line one.\n\n  Line two.\n  \n\n Line three. """
        expected = ["Line one.", "Line two.", "Line three."]
        self.assertEqual(markdown_to_blocks(markdown), expected)

class TestBlockToBlockType(unittest.TestCase):

    def test_heading(self):
        self.assertEqual(block_to_block_type("# Heading"), "heading")
        self.assertEqual(block_to_block_type("## Subheading"), "heading")
        self.assertEqual(block_to_block_type("### Another heading"), "heading")
        self.assertEqual(block_to_block_type("###### Small heading"), "heading")
        self.assertEqual(block_to_block_type("#NoSpaceAfterHash"), "paragraph")  # Invalid heading
        self.assertEqual(block_to_block_type("####### TooManyHashes"), "paragraph")  # Invalid heading

    def test_code(self):
        self.assertEqual(block_to_block_type("```code block```"), "code")
        self.assertEqual(block_to_block_type("``` python\nprint('Hello')\n```"), "code")
        self.assertEqual(block_to_block_type("```"), "paragraph")  # Invalid code block (not closed properly)
        self.assertEqual(block_to_block_type("```  "), "paragraph")  # Invalid code block (empty)

    def test_quote(self):
        self.assertEqual(block_to_block_type("> This is a quote"), "quote")
        self.assertEqual(block_to_block_type("> Another quote"), "quote")
        self.assertEqual(block_to_block_type(">Invalid quote without space"), "paragraph")  # Invalid quote
        self.assertEqual(block_to_block_type("> Multiple lines\n> with proper format"), "quote")
        self.assertEqual(block_to_block_type(">MultipleLinesNoSpace"), "paragraph")  # Invalid quote

    def test_unordered_list(self):
        self.assertEqual(block_to_block_type("* Item 1"), "unordered_list")
        self.assertEqual(block_to_block_type("- Item 2"), "unordered_list")
        self.assertEqual(block_to_block_type("* Item 1\n* Item 2"), "unordered_list")
        self.assertEqual(block_to_block_type("- Item 1\n- Item 2"), "unordered_list")
        self.assertEqual(block_to_block_type("* Item 1\n Item 2"), "paragraph")  # Invalid unordered list (no space)
        self.assertEqual(block_to_block_type("- Item 1\n-Item2"), "paragraph")  # Invalid unordered list (no space)

    def test_paragraph(self):
        self.assertEqual(block_to_block_type("This is a simple paragraph"), "paragraph")
        self.assertEqual(block_to_block_type("Random text without markdown"), "paragraph")
        self.assertEqual(block_to_block_type("Another paragraph"), "paragraph")

class TestMDToHTML(unittest.TestCase):
    def test_markdown_to_html_node(self):
        # Basic blocks
        assert markdown_to_html_node("This is a paragraph").to_html() == "<div><p>This is a paragraph</p></div>"
        assert markdown_to_html_node("# Header").to_html() == "<div><h1>Header</h1></div>"
        assert markdown_to_html_node("## Header 2").to_html() == "<div><h2>Header 2</h2></div>"

        # Lists
        assert markdown_to_html_node("- Item 1\n- Item 2").to_html() == "<div><ul><li>Item 1</li><li>Item 2</li></ul></div>"
        assert markdown_to_html_node("1. First\n2. Second").to_html() == "<div><ol><li>First</li><li>Second</li></ol></div>"

        # Code blocks
        assert markdown_to_html_node("```\ncode block\n```").to_html() == "<div><pre><code>code block</code></pre></div>"

        # Quotes
        assert markdown_to_html_node("> Quote").to_html() == "<div><blockquote>Quote</blockquote></div>"

        # Mixed inline formatting
        assert markdown_to_html_node("This is *italics* and **bold**").to_html() == "<div><p>This is <em>italics</em> and <strong>bold</strong></p></div>"

if __name__ == "__main__":
    unittest.main()
