from unittest.mock import MagicMock

from GramAddict.core.goap import GoalExecutor, ScreenType


def test_goal_executor_prevents_infinite_tab_loops(monkeypatch):
    """
    TDD Test: When attempting to navigate to a screen via a tab, if the tab
    does not lead to the required screen (e.g. reels_feed instead of follow_list),
    the GoalExecutor must NOT try the exact same tab action again in an endless loop.
    """
    device = MagicMock()
    executor = GoalExecutor(device, "test_user")
    executor.planner.knowledge.wipe()

    # We want to achieve "open following list", which requires ScreenType.FOLLOW_LIST
    # Currently on HOME_FEED
    # The heuristic might guess "tap reels tab" because of a fallback

    # Track executed actions
    executed_actions = []

    # Mock perceive to alternate between HOME_FEED and REELS_FEED
    # If we press a tab on HOME, we go to REELS.
    # If we press back on REELS, we go to HOME.
    current_screen = ScreenType.HOME_FEED

    def fake_perceive(*args, **kwargs):
        if current_screen == ScreenType.HOME_FEED:
            return {
                "screen_type": ScreenType.HOME_FEED,
                "available_actions": ["tap reels tab", "tap explore tab"],
                "context": {},
            }
        else:
            return {"screen_type": ScreenType.REELS_FEED, "available_actions": ["press back"], "context": {}}

    executor.perceive = MagicMock(side_effect=fake_perceive)

    def fake_execute(action, **kwargs):
        nonlocal current_screen
        executed_actions.append(action)
        if action == "open following list" and current_screen == ScreenType.HOME_FEED:
            current_screen = ScreenType.REELS_FEED
            return True
        elif action == "press back" and current_screen == ScreenType.REELS_FEED:
            current_screen = ScreenType.HOME_FEED
            return True
        return False

    monkeypatch.setattr(executor, "_execute_action", fake_execute)

    # Speed up sleep
    monkeypatch.setattr("GramAddict.core.goap.random_sleep", lambda x, y: None)

    # Execute the goal
    executor.max_steps = 10
    result = executor.achieve("open following list")

    # Assert it failed (we never reached FOLLOW_LIST)
    assert result is False

    # Assert we didn't loop endlessly.
    # Try 1: tap reels tab
    # Try 2: press back
    # Try 3: It should NOT try 'tap reels tab' again.

    count_open_following = executed_actions.count("open following list")
    assert (
        count_open_following == 1
    ), f"Bot is stuck in a loop! It tried to open following list {count_open_following} times."
