from GramAddict.core.telepathic_engine import TelepathicEngine


class TestGridRetryDiversity:
    """
    TDD Tests: Reproduces Bug 2 from the 2026-04-17 09:56 run.

    The Grid Fast-Path always selects the exact same node on every retry
    because it sorts by (y, x) and picks index 0. When a click fails,
    the retry should skip previously-failed positions.
    """

    def setup_method(self):
        self.engine = TelepathicEngine()

    def _make_grid_nodes(self):
        """Create 6 realistic explore grid nodes (2 rows × 3 cols)."""
        return [
            {
                "semantic_string": "id context: 'image button'",
                "x": 178,
                "y": 558,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
            {
                "semantic_string": "id context: 'image button'",
                "x": 540,
                "y": 558,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
            {
                "semantic_string": "id context: 'image button'",
                "x": 902,
                "y": 558,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
            {
                "semantic_string": "id context: 'image button'",
                "x": 178,
                "y": 1040,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
            {
                "semantic_string": "id context: 'image button'",
                "x": 540,
                "y": 1040,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
            {
                "semantic_string": "id context: 'image button'",
                "x": 902,
                "y": 1040,
                "area": 169100,
                "resource_id": "com.instagram.android:id/image_button",
                "class_name": "android.widget.Button",
                "selected": False,
                "original_attribs": {"text": "", "desc": ""},
            },
        ]

    def test_first_call_returns_topmost_leftmost(self):
        """Without skip_positions, Grid Fast-Path returns (178, 558)."""
        nodes = self._make_grid_nodes()
        result = self.engine._grid_fast_path("first image in explore grid", nodes)
        assert result is not None
        assert result["x"] == 178
        assert result["y"] == 558

    def test_retry_skips_failed_position(self):
        """With skip_positions={(178, 558)}, the next node (540, 558) is returned."""
        nodes = self._make_grid_nodes()
        result = self.engine._grid_fast_path("first image in explore grid", nodes, skip_positions={(178, 558)})
        assert result is not None
        assert (result["x"], result["y"]) == (540, 558), f"Expected (540, 558) but got ({result['x']}, {result['y']})"

    def test_skip_multiple_positions(self):
        """Skipping 2 positions returns the 3rd grid item."""
        nodes = self._make_grid_nodes()
        result = self.engine._grid_fast_path(
            "first image in explore grid", nodes, skip_positions={(178, 558), (540, 558)}
        )
        assert result is not None
        assert (result["x"], result["y"]) == (902, 558)

    def test_all_positions_skipped_returns_none(self):
        """If every grid node is skipped, return None to trigger VLM fallback."""
        nodes = self._make_grid_nodes()
        all_positions = {(n["x"], n["y"]) for n in nodes}
        result = self.engine._grid_fast_path("first image in explore grid", nodes, skip_positions=all_positions)
        assert result is None
