import os
import unittest
from unittest.mock import MagicMock, patch

from GramAddict.core.q_nav_graph import QNavGraph
from GramAddict.core.telepathic_engine import TelepathicEngine


class TestFalseLearning(unittest.TestCase):
    def setUp(self):
        # Ensure we start with clean caches for tests
        if os.path.exists("telepathic_memory.json"):
            os.remove("telepathic_memory.json")
        if os.path.exists("telepathic_blacklist.json"):
            os.remove("telepathic_blacklist.json")

        self.device = MagicMock()
        self.device.app_id = "com.instagram.android"
        self.device._get_current_app.return_value = "com.instagram.android"

        # Load Reels dump
        with open("tests/fixtures/reels_feed_dump.xml", "r") as f:
            self.reels_xml = f.read()

    def test_repro_accidental_learning_on_mismatch(self):
        """
        REPRO TEST: Verifies that QNavGraph/TelepathicEngine incorrectly learns
        a mapping if a tap on the WRONG element changes the screen.
        """
        nav = QNavGraph(self.device)
        engine = TelepathicEngine.get_instance()

        # 1. Setup: The bot wants to 'tap_like_button'
        # But we mock the engine to mistakenly return the 'Reels Tab' icon instead
        # Reels Tab icon bounds in fixture: [292,2266][355,2329]
        fake_node = {
            "x": 323,
            "y": 2297,
            "score": 0.85,
            "semantic": "id context: 'tab icon'",
            "source": "agentic_fallback",
        }

        # Define a side effect that simulates find_best_node's internal tracking
        def mock_find_best_node(xml, intent, **kwargs):
            TelepathicEngine._last_click_context = {
                "intent": intent,
                "semantic_string": fake_node["semantic"],
                "x": fake_node["x"],
                "y": fake_node["y"],
                "timestamp": 12345,
            }
            return fake_node

        with patch.object(TelepathicEngine, "find_best_node", side_effect=mock_find_best_node):
            # Simulate a UI change happening after the tap
            self.device.dump_hierarchy.side_effect = (
                [
                    self.reels_xml,  # Attempt 1: Pre-clearance
                    self.reels_xml,  # Attempt 1: Re-acquire context
                    '<hierarchy><node package="com.instagram.android" content-desc="UI CHANGED" /></hierarchy>',  # Attempt 1: Post-click
                    self.reels_xml,  # Attempt 2: Pre-clearance
                    self.reels_xml,  # Attempt 2: Re-acquire context
                    '<hierarchy><node package="com.instagram.android" content-desc="UI CHANGED" /></hierarchy>',  # Attempt 2: Post-click
                ]
                + ['<hierarchy><node package="com.instagram.android" content-desc="UI CHANGED" /></hierarchy>'] * 20
            )

            # Execute transition
            success = nav._execute_transition("tap_like_button", MagicMock())

            # success can be False or "CONTEXT_LOST" (which is truthy), so we check if it is explicitly NOT True
            self.assertNotEqual(
                success, True, "Transition should NOT be successful because semantic verification failed"
            )

            # 2. Assert: The bot should NOT have learned the wrong mapping
            memory = engine._load_json("telepathic_memory.json")
            self.assertNotIn(
                "tap like button", memory, "Should NOT have learned 'tap like button' because fix is working"
            )

            print("\n[!] VERIFICATION SUCCESSFUL: Hardened bot rejected wrong mapping for 'tap like button'")


if __name__ == "__main__":
    unittest.main()
