from unittest.mock import MagicMock

from GramAddict.core.goap import GoalExecutor, ScreenType


def test_semantic_poison_guard_rejects_hallucinations(monkeypatch):
    """
    TDD Test: Verifiziert, dass ein Klick auf 'tap messages tab', der
    versehentlich im Reels-Feed landet (Halluzination), rigoros als Gift
    verworfen wird, anstatt die Konfidenz auf 1.0 zu setzen.
    """
    device = MagicMock()
    executor = GoalExecutor(device, "test_user")

    # 1. Wir behaupten, das Device klickt erfolgreich
    device.click = MagicMock()

    # 2. Die UI ändert sich: Vor dem Klick waren wir auf Home, danach auf Reels
    # (Obwohl 'tap messages tab' zu DM_INBOX führen sollte)
    xml_home = "<hierarchy><node resource-id='home'/></hierarchy>"
    xml_reels = "<hierarchy><node resource-id='reels'/></hierarchy>"

    device.dump_hierarchy = MagicMock(side_effect=[xml_home, xml_reels])

    # Mock perceive() passend zur echten Engine, so dass es REELS erkennt
    def fake_perceive(xml=""):
        if "reels" in xml:
            return {"screen_type": ScreenType.REELS_FEED, "available_actions": [], "context": {}}
        return {"screen_type": ScreenType.HOME_FEED, "available_actions": [], "context": {}}

    executor.perceive = MagicMock(side_effect=fake_perceive)

    engine_mock = MagicMock()
    engine_mock.find_best_node.return_value = {"node": "fake_node"}
    executor.planner.knowledge.TAB_ACTIONS = {"direct_tab": "tap messages tab"}

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

    monkeypatch.setattr("GramAddict.core.telepathic_engine.TelepathicEngine.get_instance", lambda: engine_mock)

    # Führe Action aus
    result = executor._execute_action("tap messages tab", goal="open messages")

    # ASSERT: The Poison Guard SHOULD reject the navigation
    # because it landed on REELS_FEED instead of DM_INBOX.
    assert result is False, "Aktion 'tap messages tab' die nach REELS führt, MUSS False zurückgeben!"

    # ASSERT: Die Engine MUSS angewiesen werden, den Klick zu verwerfen ("Poison Guard")
    engine_mock.reject_click.assert_called_with("tap messages tab")
    engine_mock.confirm_click.assert_not_called()


def test_goap_misplaced_blame_path_execution(monkeypatch):
    """
    TDD Test: Verifiziert, dass ein korrekter erster Zwischenschritt eines Pfades
    (z.B. 'tap profile tab' das zu 'own_profile' führt)
    erfolgreich gewertet wird, auch wenn das finale Ziel ('open following list')
    noch nicht direkt dadurch erreicht wurde.
    """
    device = MagicMock()
    executor = GoalExecutor(device, "test_user")

    # Fake UI Transition: Klick auf Profile Tab öffnet das Profil
    device.dump_hierarchy = MagicMock(side_effect=["<home/>", "<profile/>", "<profile/>"])
    device.click = MagicMock()

    def fake_perceive(xml=""):
        if "profile" in xml:
            return {"screen_type": ScreenType.OWN_PROFILE, "available_actions": [], "context": {}}
        return {"screen_type": ScreenType.HOME_FEED, "available_actions": [], "context": {}}

    executor.perceive = MagicMock(side_effect=fake_perceive)

    engine_mock = MagicMock()
    engine_mock.find_best_node.return_value = {"node": "fake_node"}
    monkeypatch.setattr("GramAddict.core.telepathic_engine.TelepathicEngine.get_instance", lambda: engine_mock)

    # Die Navigation führt zu ScreenType.OWN_PROFILE, Ziel ist "open following list".
    monkeypatch.setattr("GramAddict.core.goap.time.sleep", lambda x: None)

    # _execute_recalled_path ruft _execute_action mehrfach auf.
    # Für den Test prüfen wir direkt was _execute_action beim ERSTEN Schritt macht:

    # Simuliere 'tap profile tab' während unser Langzeitziel 'open following list' ist!
    result = executor._execute_action("tap profile tab", goal="open following list")

    # ASSERT: Das MUß True sein, da die UI sich entscheidend zu einem gültigen Zustand bewegt hat!
    assert (
        result is True
    ), "Misplaced Blame! legitimer Teilschritt wurde als Fehlschlag verworfen, weil das Endziel nicht direkt erreicht wurde."
    engine_mock.confirm_click.assert_called_with("tap profile tab")
    engine_mock.reject_click.assert_not_called()
