"""
🔴 RED TDD: VLM Structural Guard Inconsistency + GOAP Back-Press Loop

Bug A: VLM guard enforces "must be at bottom" for ALL nav intent keywords,
       but "following"/"follower" are PROFILE STATS, not nav tabs. The inner
       _structural_sanity_check correctly only enforces for "tab" intents.

Bug B: GOAP back-press loop has no circuit breaker. If the planner keeps
       pressing back on the same screen, it eventually exits Instagram.
"""

from unittest.mock import MagicMock

from GramAddict.core.goap import GoalExecutor, ScreenType
from GramAddict.core.telepathic_engine import TelepathicEngine


class TestVlmGuardFollowingConsistency:
    """The VLM guard must NOT reject 'following' count elements at the top of the screen."""

    def test_following_intent_allows_top_screen_elements(self):
        """
        The 'following' count on a profile is at Y≈246 (top 10%).
        The VLM Structural Guard must NOT reject this as a 'hallucinated nav tab'.
        It's a profile stat, not a tab.
        """
        engine = TelepathicEngine()
        screen_height = 2424

        # Simulate the exact node the VLM found
        following_node = {
            "semantic_string": "text: '2,285 following', id context: 'row profile header following container'",
            "y": 246,
            "area": 5000,
            "class_name": "android.widget.TextView",
            "resource_id": "row_profile_header_following_container",
        }

        intent = "tap following list"

        # Inner structural guard should accept this (it has the "tab" check)
        is_valid = engine._structural_sanity_check(following_node, intent, screen_height)
        assert is_valid is True, (
            "Inner structural guard rejected 'following' element at Y=246. "
            "The intent 'tap following list' does not contain 'tab', so the nav-tab enforcement should not apply."
        )

    def test_follower_intent_allows_top_screen_elements(self):
        """Same bug for 'follower' count on a profile."""
        engine = TelepathicEngine()
        screen_height = 2424

        follower_node = {
            "semantic_string": "text: '2,622 followers', id context: 'row profile header followers container'",
            "y": 246,
            "area": 5000,
            "class_name": "android.widget.TextView",
            "resource_id": "row_profile_header_followers_container",
        }

        intent = "open followers list"

        is_valid = engine._structural_sanity_check(follower_node, intent, screen_height)
        assert is_valid is True, "Inner structural guard rejected 'followers' element at Y=246."


class TestGoapBackPressCircuitBreaker:
    """GOAP must detect and abort back-press loops on the same screen."""

    def test_consecutive_back_presses_on_same_screen_aborts(self):
        """
        If the GOAP planner presses back 3+ times on the same screen type
        without any screen transition, it should abort instead of continuing
        to press back until it exits the app.
        """
        device = MagicMock()
        device.app_id = "com.instagram.android"
        device._get_current_app.return_value = "com.instagram.android"

        goap = GoalExecutor(device, bot_username="testbot")
        goap._sae = MagicMock()
        goap._sae.ensure_clear_screen.return_value = True

        # Simulate being stuck on HOME_FEED with only back available
        call_count = 0

        def mock_perceive():
            nonlocal call_count
            call_count += 1
            return {
                "screen_type": ScreenType.HOME_FEED,
                "available_actions": ["press back"],
                "context": {},
                "selected_tab": "feed_tab",
            }

        goap.perceive = mock_perceive

        # Mock _execute_action to always succeed (pressing back "works" but stays on same screen)
        goap._execute_action = MagicMock(return_value=True)

        result = goap.achieve("open following list", max_steps=15)

        # The bot should NOT have pressed back more than 3 times
        back_calls = [c for c in goap._execute_action.call_args_list if c[0][0] == "press back"]
        assert len(back_calls) <= 3, (
            f"GOAP pressed back {len(back_calls)} times on the same screen. "
            "It should abort after 3 consecutive back-presses with no progress."
        )
        assert result is False, "GOAP should fail when stuck in a back-press loop."
