from unittest.mock import MagicMock, patch

import pytest


@pytest.fixture
def mock_device():
    from GramAddict.core.physics.biomechanics import PhysicsBody
    from GramAddict.core.physics.sendevent_injector import SendEventInjector

    PhysicsBody.reset()
    SendEventInjector.reset()

    device = MagicMock()
    device.deviceV2 = MagicMock()
    device.get_info.return_value = {"displayWidth": 1080, "displayHeight": 2400}
    device.app_id = "com.instagram.android"
    device.shell = MagicMock(return_value="")

    yield device

    PhysicsBody.reset()
    SendEventInjector.reset()


def test_reels_loop_repost_execution(mock_device):
    """
    TDD Test: Verifies that the bot attempts to repost a Reel if resonance is high
    and repost_percentage allows it.
    """
    # 1. Setup Cognitive Stack & Configs
    mock_cognitive_stack = {
        "dopamine": MagicMock(),
        "resonance": MagicMock(),
        "zero_engine": MagicMock(),
        "nav_graph": MagicMock(),
        "telepathic": MagicMock(),
        "growth_brain": MagicMock(),
        "darwin": MagicMock(),
        "active_inference": MagicMock(),
        "swarm": MagicMock(),
        "radome": MagicMock(),
        "crm": MagicMock(),
    }

    # Configure growth_brain to allow repost, disallow comment/double-tap
    mock_cognitive_stack["growth_brain"].wants_to_double_tap.return_value = False
    mock_cognitive_stack["growth_brain"].wants_to_repost.return_value = True
    mock_cognitive_stack["growth_brain"].wants_to_comment.return_value = False
    mock_cognitive_stack["growth_brain"].evaluate_hesitation.return_value = False
    mock_cognitive_stack["radome"].sanitize_xml.side_effect = lambda x: x

    # Simulate a single post interaction then exit
    mock_cognitive_stack["dopamine"].is_app_session_over.side_effect = [False, True]
    mock_cognitive_stack["dopamine"].wants_to_change_feed.return_value = False
    mock_cognitive_stack["dopamine"].wants_to_doomscroll.return_value = False

    # High resonance to trigger repost
    mock_cognitive_stack["resonance"].calculate_resonance.return_value = 0.95

    configs = MagicMock()
    configs.args.repost_percentage = 100
    configs.args.interact_percentage = 100
    configs.args.likes_percentage = 0
    configs.args.comment_percentage = 0
    configs.args.follow_percentage = 0
    configs.args.visit_profiles = 0

    session_state = MagicMock()
    session_state.check_limit.return_value = (False, False, False, False)

    # 2. Mock Reels UI
    # Reels usually have 'clips_viewer' or similar in the hierarchy
    reels_xml = """<?xml version='1.0' ?>
    <hierarchy>
        <node resource-id="com.instagram.android:id/row_feed_profile_header" bounds="[0,200][1080,300]" />
        <node resource-id="com.instagram.android:id/clips_author_username" text="test_user" />
        <node resource-id="com.instagram.android:id/clips_media_component" content-desc="Check out this cool reel #repost #viral" />
        <node resource-id="com.instagram.android:id/clips_video_container" />
        <node resource-id="com.instagram.android:id/direct_share_button" content-desc="Share" />
    </hierarchy>"""

    mock_device.dump_hierarchy.return_value = reels_xml
    mock_device.dump_hierarchy.return_value = reels_xml

    # Repost Sheet XML
    repost_sheet_xml = """<?xml version='1.0' ?>
    <hierarchy>
        <node resource-id="com.instagram.android:id/bottom_sheet_container">
            <node text="Repost" content-desc="Repost interaction button with two arrows" />
        </node>
    </hierarchy>"""

    # 3. Setup Telepathic Engine Mocks
    mock_telepathic = mock_cognitive_stack["telepathic"]
    # First call: find interaction buttons
    mock_telepathic._extract_semantic_nodes.return_value = [{"x": 100, "y": 100, "semantic_string": "share button"}]

    # Logic for finding nodes — return proper attributes for content extraction
    def mock_find_best_node(xml, intent, **kwargs):
        intent_lower = intent.lower() if isinstance(intent, str) else ""
        if "repost" in intent_lower:
            return {
                "x": 500,
                "y": 2000,
                "bounds": "[400,1950][600,2050]",
                "skip": False,
                "original_attribs": {"text": "Repost", "desc": ""},
            }
        if "author" in intent_lower or "username" in intent_lower:
            return {
                "x": 100,
                "y": 250,
                "text": "test_user",
                "content-desc": "",
                "bounds": "[0,200][1080,300]",
                "skip": False,
                "original_attribs": {"text": "test_user", "desc": ""},
            }
        if "media" in intent_lower or "image" in intent_lower or "video" in intent_lower:
            return {
                "x": 540,
                "y": 1200,
                "text": "",
                "content-desc": "Check out this cool reel #repost #viral",
                "bounds": "[0,300][1080,2100]",
                "skip": False,
                "original_attribs": {"text": "", "desc": "Check out this cool reel #repost #viral"},
            }
        return {"x": 100, "y": 100, "bounds": "[90,90][110,110]", "skip": False}

    mock_telepathic.find_best_node.side_effect = mock_find_best_node

    # Simulate share button transition success
    mock_cognitive_stack["nav_graph"]._execute_transition.return_value = True

    # 4. Execute Feed Loop for Reels
    with (
        patch("GramAddict.core.bot_flow.TelepathicEngine") as MockEngine,
        patch("GramAddict.core.telepathic_engine.TelepathicEngine.get_instance", return_value=mock_telepathic),
        patch("GramAddict.core.bot_flow._humanized_click") as mock_click,
        patch("GramAddict.core.bot_flow.sleep"),
        patch("GramAddict.core.physics.timing.sleep"),
        patch("GramAddict.core.bot_flow.dump_ui_state"),
    ):
        MockEngine.get_instance.return_value = mock_telepathic

        # Resilient state-based mock for dump_hierarchy
        comment_sheet_xml = """<?xml version='1.0' ?><hierarchy><node resource-id="com.instagram.android:id/layout_comment_thread" /><node resource-id="com.instagram.android:id/bottom_sheet_container" /></hierarchy>"""

        state = {"current": reels_xml}

        def side_effect_func(*args, **kwargs):
            return state["current"]

        mock_device.dump_hierarchy.side_effect = side_effect_func

        # We need to change the state when transition is called
        mock_cognitive_stack["nav_graph"]._execute_transition

        def mocked_execute(transition_name):
            if transition_name == "tap_comment_button":
                state["current"] = comment_sheet_xml
            elif transition_name == "tap_share_button":
                state["current"] = repost_sheet_xml
            return True

        mock_cognitive_stack["nav_graph"]._execute_transition.side_effect = mocked_execute

        from GramAddict.core.bot_flow import _run_zero_latency_feed_loop

        _run_zero_latency_feed_loop(
            mock_device,
            mock_cognitive_stack["zero_engine"],
            mock_cognitive_stack["nav_graph"],
            configs,
            session_state,
            "ReelsFeed",
            mock_cognitive_stack,
            is_reels=True,
        )

        # 5. Assertions
        # Should click the share button (via transition) and the repost button (direct click)
        assert mock_cognitive_stack["nav_graph"]._execute_transition.called_with("tap_share_button")

        # Check if _humanized_click was called for the Repost button (x=500, y=2000)
        click_args = [call.args for call in mock_click.call_args_list]
        repost_clicked = any(args[1] == 500 and args[2] == 2000 for args in click_args)

        assert repost_clicked, "Repost button was not clicked on Reels share sheet"
        assert mock_telepathic.confirm_click.called_with("Repost interaction button with two arrows")
