from unittest.mock import MagicMock, patch

from GramAddict.core.goap import GoalExecutor
from GramAddict.core.screen_topology import ScreenType


def test_goap_unlearns_transition_on_back_press_trap():
    """Prove that GOAP forgets a specific topological transition when it gets trapped and spams 'back'."""
    device = MagicMock()
    orchestrator = GoalExecutor(device, "testuser")

    # Mocking internal state
    goal = "open messages"

    def mock_exec(*args, **kwargs):
        print("EXECUTING:", args, kwargs)
        return True

    orchestrator._execute_action = MagicMock(side_effect=mock_exec)  # "press back" succeeds
    orchestrator.path_memory = MagicMock()
    orchestrator.path_memory.recall_path.return_value = None
    # To test the back-press circuit breaker, we just need to feed it 3 "press back" actions.

    # Since achieve is complex, let's just test that the required logic
    # exists inside it. The circuit breaker is in the "EXECUTE" block of achieve.
    # We will mock the planner to return "press back" 3 times.
    orchestrator.planner.plan_next_step = MagicMock(side_effect=[["press back"], ["press back"], ["press back"], []])
    orchestrator.perceive = MagicMock(
        return_value={"screen_type": ScreenType.EXPLORE_GRID, "available_actions": ["mock"]}
    )

    with patch("GramAddict.core.qdrant_memory.NavigationMemoryDB") as MockNavDB:
        mock_nav_instance = MockNavDB.return_value

        # We need to simulate that `steps_taken` already had "tap explore tab"
        # However, achieve starts with an empty `steps_taken`.
        # So we mock the internal variables if possible, but they are local.
        # Alternatively, we make the planner return "tap explore tab", then 3 "press back"s.
        orchestrator.planner.plan_next_step = MagicMock(
            side_effect=["tap explore tab", "press back", "press back", "press back", "press back", None]
        )

        # Act
        result = orchestrator.achieve(goal, max_steps=10)

        # Assert (RED)
        assert result is False

        # Did it forget the path? (learn_path with success=False)
        assert orchestrator.path_memory.learn_path.called

        # Did it unlearn the transition?
        print("MockNavDB calls:", MockNavDB.mock_calls)
        print("NavInstance calls:", mock_nav_instance.mock_calls)
        mock_nav_instance.unlearn_transition.assert_called_once_with(ScreenType.EXPLORE_GRID.value, "tap explore tab")
