class HTMLNode:
    def __init__(self, tag=None, value=None, children=None, props=None):
        self.tag = tag
        self.value = value
        self.children = children or []
        self.props = props or {}

    def to_html(self):
        raise NotImplementedError

    def props_to_html(self):
        if not self.props:
            return ""
        return " ".join([f'{key}="{value}"' for key, value in self.props.items()])
    
    def __repr__(self):
        return f"Tag: {self.tag}, Val: {self.value}, Children: {self.children}, Props: {self.props}"

class LeafNode(HTMLNode):
    def __init__(self, value,tag=None):
        super().__init__(tag,value,children=[])
    
    def to_html(self):
        props_as_html = self.props_to_html()
        
        if not self.value:
            raise ValueError("Value is missing")
        
        if not self.tag:
            return f"{self.value}"
        
        if props_as_html == "":
            return f"<{self.tag}>{self.value}</{self.tag}>"
        else:
            return f"<{self.tag} {props_as_html}>{self.value}</{self.tag}>"

class ParentNode(HTMLNode):
    def __init__(self, tag, children, props=None):
        super().__init__(tag=tag, children=children, props=props or {})

    def to_html(self):
        if not self.tag:
            raise ValueError("Tag cannot be None!")
        if not self.children:
            raise ValueError("Children are missing!")

        props_html = self.props_to_html()
        children_html = "".join(
            child.to_html() if isinstance(child, HTMLNode) else child
            for child in self.children
        )

        if props_html:
            return f"<{self.tag} {props_html}>{children_html}</{self.tag}>"
        else:
            return f"<{self.tag}>{children_html}</{self.tag}>"
