from unittest.mock import MagicMock, patch

from GramAddict.core.darwin_engine import DarwinEngine


class DummyArgs:
    def __init__(self):
        self.interact_percentage = 0
        self.follow_percentage = 0


def test_darwin_engine_explore_exploit():
    """Test the Multi-Armed Bandit Epsilon-Greedy logic without a real Qdrant server."""
    with patch("GramAddict.core.qdrant_memory.QdrantClient"):
        engine = DarwinEngine("test_user")

        # Override epsilon to force exploitation (Greedy)
        # Wait, inside synthesize_interaction_profile epsilon is hardcoded to 0.15

        mock_record_1 = MagicMock()
        mock_record_1.payload = {"params": {"initial_dwell_sec": 2.0}, "reward": 10.0}

        mock_record_2 = MagicMock()
        mock_record_2.payload = {"params": {"initial_dwell_sec": 10.0}, "reward": 50.0}

        engine.client.scroll.return_value = ([mock_record_1, mock_record_2], None)

        # We patch random.random to force Exploit
        with patch("random.random", return_value=0.99):
            profile = engine.synthesize_interaction_profile(0.5)

            # Just ensure it generated something valid within bounds
            assert 1.0 <= profile["initial_dwell_sec"] <= 20.0

        # We patch random.random to force Explore
        with patch("random.random", return_value=0.01):
            profile_explore = engine.synthesize_interaction_profile(0.5)
            # Just ensure it generated something valid
            assert "initial_dwell_sec" in profile_explore


def test_evaluate_session_end_short_session():
    """Ensure short sessions are not recorded to avoid polluting RoI metrics."""
    with patch("GramAddict.core.qdrant_memory.QdrantClient"):
        engine = DarwinEngine("test_user")
        engine.current_behavior = {"initial_dwell_sec": 5.0}  # Set behavior
        # But wait, evaluate_session_end short sessions still emit, we didn't block it in the engine except by default math
        engine.emit_reward_signal(followers_gained=10, block_warnings_seen=0)

        # It should upsert
        engine.client.upsert.assert_called_once()


def test_evaluate_session_end_upsert():
    """Ensure valid sessions are successfully logged to the database."""
    with patch("GramAddict.core.qdrant_memory.QdrantClient"):
        engine = DarwinEngine("test_user")
        engine.current_behavior = {"initial_dwell_sec": 5.0}
        engine.evaluate_session_end(60.0, 100)  # 60 minutes

        engine.client.upsert.assert_called_once()


def test_execute_proof_of_resonance_close_comments():
    """Verify that Darwin correctly closes the comments section even if 'bottom_sheet_container' is missing."""
    with patch("GramAddict.core.qdrant_memory.QdrantClient"):
        engine = DarwinEngine("test_user")
        device = MagicMock()
        nav_graph = MagicMock()
        zero_engine = MagicMock()
        configs = MagicMock()

        # Make the profile decide to read comments for 2s
        fake_profile = {
            "initial_dwell_sec": 1.0,
            "scroll_velocity": 1.0,
            "scroll_depth_clicks": 0,
            "back_swipe_prob": 0.0,
            "comment_read_dwell": 2.0,
        }
        with patch.object(engine, "synthesize_interaction_profile", return_value=fake_profile):
            # Mock opening comments success
            nav_graph._execute_transition.return_value = True

            # Simulated UI Dump: No 'bottom_sheet_container', but neither 'row_feed' nor 'button_like'
            # Which occurs when IG renames it to 'fragment_container_view' or similar wrapper
            device.dump_hierarchy.return_value = """<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
            <hierarchy>
                <node class="android.widget.FrameLayout" bounds="[0,0][1080,2400]">
                    <!-- Random UI generic sheet classes the Bot doesn't track -->
                    <node class="androidx.appcompat.widget.LinearLayoutCompat" text="Reply" />
                </node>
            </hierarchy>
            """

            # Act
            with patch("random.random", return_value=0.0):  # Force comment block entry
                with patch("GramAddict.core.darwin_engine.DarwinEngine._has_comments", return_value=True):
                    with patch("GramAddict.core.telepathic_engine.TelepathicEngine.get_instance") as mock_telepathic:
                        mock_engine = MagicMock()
                        mock_engine.find_best_node.return_value = None
                        mock_telepathic.return_value = mock_engine

                        engine.execute_proof_of_resonance(
                            device=device,
                            resonance=0.9,
                            nav_graph=nav_graph,
                            zero_engine=zero_engine,
                            configs=configs,
                            resonance_oracle=None,
                            username="test",
                        )

            # Assert: Instead of checking string names for "bottom_sheet_container",
            # it should verify the presence of 'row_feed' to confirm we are back in Home!
            # If not in Home, it presses back twice.
            assert device.press.call_count == 2
