from GramAddict.core.perception.spatial_parser import SpatialNode, SpatialParser

XML_FIXTURE = """<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
  <node index="0" bounds="[0,0][1080,2400]" class="android.widget.FrameLayout">
    <node index="0" bounds="[0,2200][1080,2400]" class="android.view.ViewGroup" content-desc="Bottom Navigation">
      <node index="0" bounds="[50,2250][150,2350]" class="android.widget.ImageView" content-desc="Home Tab" clickable="true"/>
      <node index="1" bounds="[250,2250][350,2350]" class="android.widget.ImageView" content-desc="Explore Tab" clickable="true"/>
    </node>
    <node index="1" bounds="[0,100][1080,2200]" class="androidx.recyclerview.widget.RecyclerView">
      <node index="0" bounds="[50,150][1030,800]" class="android.widget.FrameLayout" content-desc="Post 1">
        <node index="0" bounds="[100,700][200,750]" class="android.widget.Button" text="Like" clickable="true"/>
      </node>
    </node>
  </node>
</hierarchy>
"""


class TestSpatialParser:
    def test_parses_xml_into_spatial_nodes(self):
        parser = SpatialParser()
        root = parser.parse(XML_FIXTURE)

        assert root is not None
        assert isinstance(root, SpatialNode)
        assert root.bounds == (0, 0, 1080, 2400)
        assert len(root.children) == 2  # The Bottom Nav and the Recycler View

    def test_extracts_all_clickable_nodes(self):
        parser = SpatialParser()
        root = parser.parse(XML_FIXTURE)
        clickables = parser.get_clickable_nodes(root)

        assert len(clickables) == 3
        descriptions = [n.content_desc or n.text for n in clickables]
        assert "Home Tab" in descriptions
        assert "Explore Tab" in descriptions
        assert "Like" in descriptions

    def test_spatial_containment(self):
        parser = SpatialParser()
        root = parser.parse(XML_FIXTURE)

        # Get the first post
        post_nodes = [n for n in parser.get_all_nodes(root) if n.content_desc == "Post 1"]
        assert len(post_nodes) == 1
        post = post_nodes[0]

        # Get the Like button
        like_nodes = [n for n in parser.get_all_nodes(root) if n.text == "Like"]
        assert len(like_nodes) == 1
        like_btn = like_nodes[0]

        # Spatial Containment: The Like button should be mathematically inside the Post
        assert post.contains(like_btn) is True

        # The Like button should NOT contain the Post
        assert like_btn.contains(post) is False

        # The Home Tab should NOT be in the Post
        home_tabs = [n for n in parser.get_all_nodes(root) if n.content_desc == "Home Tab"]
        assert post.contains(home_tabs[0]) is False

    def test_spatial_intersection(self):
        SpatialParser()

        # Node 1: Left side
        n1 = SpatialNode(bounds=(0, 0, 100, 100))
        # Node 2: Overlapping Right side
        n2 = SpatialNode(bounds=(50, 50, 150, 150))
        # Node 3: Far away
        n3 = SpatialNode(bounds=(200, 200, 300, 300))

        assert n1.intersects(n2) is True
        assert n2.intersects(n1) is True
        assert n1.intersects(n3) is False
