import logging
from unittest.mock import MagicMock

import pytest

from GramAddict.core.bot_flow import _interact_with_profile
from GramAddict.core.exceptions import ActionBlockedError
from GramAddict.core.q_nav_graph import QNavGraph


class TestCriticalAnomalyGuards:
    def setup_method(self):
        self.mock_device = MagicMock()
        self.mock_device.deviceV2 = MagicMock()
        self.mock_device.app_id = "com.instagram.android"
        self.mock_device._get_current_app.return_value = "com.instagram.android"
        self.mock_device.get_info.return_value = {"displayWidth": 1080, "displayHeight": 2400}

        self.nav_graph = QNavGraph(self.mock_device)
        self.logger = logging.getLogger("test")

    def test_action_blocked_dialog_raises_exception(self):
        """
        If the OS/App shows a 'Try Again Later' block, we must explicitly crash the bot
        by throwing an ActionBlockedError to prevent spamming and risking permanent bans.
        """
        self.mock_device.dump_hierarchy.return_value = """
        <hierarchy>
          <node package="com.instagram.android" resource-id="com.instagram.android:id/dialog_container">
            <node package="com.instagram.android" text="Try Again Later" resource-id="com.instagram.android:id/dialog_title" />
            <node package="com.instagram.android" text="We restrict certain activity to protect our community." />
            <node package="com.instagram.android" text="OK" resource-id="com.instagram.android:id/button_positive" />
          </node>
        </hierarchy>
        """

        with pytest.raises(ActionBlockedError, match="Instagram action block detected"):
            self.nav_graph._clear_anomaly_obstacles()

    def test_interact_with_private_profile_aborts(self):
        """
        If a user account is private, _interact_with_profile must skip everything
        and return without doing ANY interactions.
        """
        self.mock_device.dump_hierarchy.return_value = """
        <hierarchy>
          <node text="marisaundmarc" />
          <node text="This account is private" bounds="[100,500][900,600]" />
          <node text="Follow to see their photos and videos." />
        </hierarchy>
        """

        configs = MagicMock()
        session_state = MagicMock()

        _interact_with_profile(
            self.mock_device, configs, "test_private", session_state, sleep_mod=0.0, logger=self.logger
        )

        # Verify it did not attempt to find stories, scrape, or anything
        self.mock_device.click.assert_not_called()
        self.mock_device.press.assert_not_called()

    def test_interact_with_empty_profile_aborts(self):
        """
        If a user account has 0 posts, we must skip.
        """
        self.mock_device.dump_hierarchy.return_value = """
        <hierarchy>
          <node text="marisaundmarc" />
          <node text="No posts yet" bounds="[100,500][900,600]" />
        </hierarchy>
        """

        configs = MagicMock()
        session_state = MagicMock()

        _interact_with_profile(
            self.mock_device, configs, "test_empty", session_state, sleep_mod=0.0, logger=self.logger
        )

        self.mock_device.click.assert_not_called()
        self.mock_device.press.assert_not_called()

    def test_comments_disabled_guard(self):
        """
        If the user has disabled comments, 'tap_comment_button' must abort and return skip=True
        instead of defaulting to the VLM fallback which hallucinates clicks on random UI elements.
        """
        from GramAddict.core.telepathic_engine import TelepathicEngine

        engine = TelepathicEngine()  # Bypass singleton mock

        xml_dump = """
        <hierarchy>
          <node text="marisaundmarc" />
          <node text="comments are turned off." bounds="[100,500][900,600]" />
          <node text="Following" />
        </hierarchy>
        """

        # TelepathicEngine should instantly return a "skip" object without invoking memory or vectors
        result = engine.find_best_node(xml_dump, "tap comment button", device=self.mock_device)

        assert result is not None, "Engine completely failed instead of returning skip"
        assert result.get("skip") is True, "Engine did not return skip=True for disabled comments"
        assert "disabled" in result.get("semantic", ""), "Semantic tag should reflect disabled comments"
