"""
TDD: Behavior Plugins Tests.

Tests all concrete behavior plugins:
- ProfileGuardPlugin (safety gates)
- StoryViewPlugin (story watching)
- FollowPlugin (follow interaction)
- GridLikePlugin (grid liking)
- Physics timing module (wait/align)
"""

from unittest.mock import MagicMock, patch

import pytest

from GramAddict.core.behaviors import BehaviorContext, PluginRegistry


@pytest.fixture
def device():
    dev = MagicMock()
    dev.get_info.return_value = {"displayWidth": 1080, "displayHeight": 2400}
    dev.dump_hierarchy.return_value = '<hierarchy><node resource-id="row_feed_photo_profile_name"/></hierarchy>'
    dev.shell = MagicMock()
    dev.cm_to_pixels.return_value = 5
    return dev


@pytest.fixture
def configs():
    c = MagicMock()
    c.args = MagicMock()
    c.args.carousel_percentage = "0"
    c.args.stories_percentage = "0"
    c.args.follow_percentage = "0"
    c.args.likes_percentage = "0"
    c.args.ignore_close_friends = False
    c.args.visual_vibe_check_percentage = "0"
    c.args.scrape_profiles = False
    return c


@pytest.fixture
def session_state():
    ss = MagicMock()
    ss.my_username = "testbot"
    ss.totalFollowed = {}
    ss.totalLikes = 0
    ss.check_limit.return_value = False
    return ss


@pytest.fixture
def ctx(device, configs, session_state):
    mock_nav = MagicMock()
    mock_nav.current_state = "ProfileView"
    return BehaviorContext(
        device=device,
        configs=configs,
        session_state=session_state,
        cognitive_stack={"nav_graph": mock_nav},
        context_xml='<hierarchy><node resource-id="com.instagram.android:id/profile_header" /><node resource-id="row_feed_photo_profile_name"/></hierarchy>',
        sleep_mod=1.0,
        username="target_user",
    )


# ── Profile Guard Tests ──


class TestProfileGuardPlugin:
    def test_blocks_self_profile(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.username = "testbot"
        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.executed is True
        assert result.should_skip is True
        assert result.metadata["reason"] == "self_profile"

    def test_blocks_private_account(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.context_xml = "<hierarchy>This account is private</hierarchy>"
        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.executed is True
        assert result.should_skip is True
        assert result.metadata["reason"] == "private"

    def test_blocks_private_account_german(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.context_xml = "<hierarchy>Dieses Konto ist privat</hierarchy>"
        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.executed is True
        assert result.metadata["reason"] == "private"

    def test_blocks_empty_account(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.context_xml = "<hierarchy>No Posts Yet</hierarchy>"
        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.should_skip is True
        assert result.metadata["reason"] == "empty"

    def test_blocks_close_friend(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.configs.args.ignore_close_friends = True
        ctx.context_xml = "<hierarchy>Close Friend badge visible</hierarchy>"
        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.should_skip is True
        assert result.metadata["reason"] == "close_friend"

    def test_passes_valid_profile(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        plugin = ProfileGuardPlugin()
        result = plugin.execute(ctx)
        assert result.executed is False  # No guard triggered

    def test_is_exclusive(self):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        plugin = ProfileGuardPlugin()
        assert plugin.exclusive is True
        assert plugin.priority == 100

    def test_does_not_activate_without_username(self, ctx):
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        ctx.username = ""
        plugin = ProfileGuardPlugin()
        assert plugin.can_activate(ctx) is False


# ── Story View Tests ──


class TestStoryViewPlugin:
    def test_does_not_activate_when_disabled(self, ctx):
        from GramAddict.core.behaviors.story_view import StoryViewPlugin

        ctx.configs.args.stories_percentage = "0"
        plugin = StoryViewPlugin()
        assert plugin.can_activate(ctx) is False

    def test_activates_when_enabled(self, ctx):
        from GramAddict.core.behaviors.story_view import StoryViewPlugin

        ctx.configs.args.stories_percentage = "50"
        plugin = StoryViewPlugin()
        assert plugin.can_activate(ctx) is True

    def test_skips_when_no_story_ring(self, ctx):
        from GramAddict.core.behaviors.story_view import StoryViewPlugin

        ctx.configs.args.stories_percentage = "100"
        ctx.context_xml = "<hierarchy>No stories here</hierarchy>"
        plugin = StoryViewPlugin()
        result = plugin.execute(ctx)
        # Either random skip or no story found
        assert result.metadata.get("reason") in ("no_story", None) or result.executed is False

    def test_priority_before_follow(self):
        from GramAddict.core.behaviors.follow import FollowPlugin
        from GramAddict.core.behaviors.story_view import StoryViewPlugin

        assert StoryViewPlugin().priority < FollowPlugin().priority  # 40 < 60 — but stories run first


# ── Follow Tests ──


class TestFollowPlugin:
    def test_does_not_activate_when_disabled(self, ctx):
        from GramAddict.core.behaviors.follow import FollowPlugin

        ctx.configs.args.follow_percentage = "0"
        plugin = FollowPlugin()
        assert plugin.can_activate(ctx) is False

    def test_does_not_activate_at_limit(self, ctx):
        from GramAddict.core.behaviors.follow import FollowPlugin

        ctx.configs.args.follow_percentage = "100"
        ctx.session_state.check_limit.return_value = True
        plugin = FollowPlugin()
        assert plugin.can_activate(ctx) is False

    def test_activates_when_enabled_and_below_limit(self, ctx):
        from GramAddict.core.behaviors.follow import FollowPlugin

        ctx.configs.args.follow_percentage = "50"
        ctx.session_state.check_limit.return_value = False
        plugin = FollowPlugin()
        assert plugin.can_activate(ctx) is True

    def test_follow_success(self, ctx):
        import random

        from GramAddict.core.behaviors.follow import FollowPlugin

        random.seed(42)

        ctx.configs.args.follow_percentage = "100"
        plugin = FollowPlugin()

        with patch("GramAddict.core.behaviors.follow.sleep"):
            with patch("GramAddict.core.q_nav_graph.QNavGraph") as MockNav:
                MockNav.return_value.do.return_value = True
                result = plugin.execute(ctx)

        assert result.executed is True
        assert result.metadata["followed"] == "target_user"

    def test_priority(self):
        from GramAddict.core.behaviors.follow import FollowPlugin

        assert FollowPlugin().priority == 60


# ── Grid Like Tests ──


class TestGridLikePlugin:
    def test_does_not_activate_when_disabled(self, ctx):
        from GramAddict.core.behaviors.grid_like import GridLikePlugin

        ctx.configs.args.likes_percentage = "0"
        plugin = GridLikePlugin()
        assert plugin.can_activate(ctx) is False

    def test_does_not_activate_at_limit(self, ctx):
        from GramAddict.core.behaviors.grid_like import GridLikePlugin

        ctx.configs.args.likes_percentage = "100"
        ctx.session_state.check_limit.return_value = True
        plugin = GridLikePlugin()
        assert plugin.can_activate(ctx) is False

    def test_activates_when_enabled(self, ctx):
        from GramAddict.core.behaviors.grid_like import GridLikePlugin

        ctx.configs.args.likes_percentage = "50"
        plugin = GridLikePlugin()
        assert plugin.can_activate(ctx) is True

    def test_priority_after_follow(self):
        from GramAddict.core.behaviors.follow import FollowPlugin
        from GramAddict.core.behaviors.grid_like import GridLikePlugin

        assert GridLikePlugin().priority < FollowPlugin().priority  # 50 < 60


# ── Physics Timing Tests ──


class TestTimingModule:
    def test_wait_for_post_detects_feed(self, device):
        from GramAddict.core.physics.timing import wait_for_post_loaded

        device.dump_hierarchy.return_value = '<hierarchy><node resource-id="row_feed_photo_profile_name"/></hierarchy>'
        result = wait_for_post_loaded(device, timeout=1)
        assert result is True

    def test_wait_for_post_timeout(self, device):
        from GramAddict.core.physics.timing import wait_for_post_loaded

        device.dump_hierarchy.return_value = "<hierarchy>nothing here</hierarchy>"
        with patch("GramAddict.core.diagnostic_dump.dump_ui_state"):
            result = wait_for_post_loaded(device, timeout=0.1)
        assert result is False

    def test_wait_for_story_detects_viewer(self, device):
        from GramAddict.core.physics.timing import wait_for_story_loaded

        device.dump_hierarchy.return_value = "<hierarchy>reel_viewer_root</hierarchy>"
        result = wait_for_story_loaded(device, timeout=1)
        assert result is True

    def test_wait_for_story_timeout(self, device):
        from GramAddict.core.physics.timing import wait_for_story_loaded

        device.dump_hierarchy.return_value = "<hierarchy>no story</hierarchy>"
        result = wait_for_story_loaded(device, timeout=0.1)
        assert result is False

    def test_align_post_with_no_header(self, device):
        from GramAddict.core.physics.timing import align_active_post

        with patch("GramAddict.core.telepathic_engine.TelepathicEngine.get_instance") as mock:
            mock.return_value.find_best_node.return_value = None
            result = align_active_post(device)
        assert result is False

    def test_backward_compat_wait_from_bot_flow(self):
        """_wait_for_post_loaded must still be importable from bot_flow."""
        from GramAddict.core.bot_flow import _wait_for_post_loaded

        assert callable(_wait_for_post_loaded)

    def test_backward_compat_align_from_bot_flow(self):
        """_align_active_post must still be importable from bot_flow."""
        from GramAddict.core.bot_flow import _align_active_post

        assert callable(_align_active_post)


# ── Full Registry Integration ──


class TestFullPluginStack:
    """End-to-end: register all plugins, execute on a profile."""

    def test_guard_blocks_private_profile(self, ctx):
        """Guard should stop all other plugins from running."""
        from GramAddict.core.behaviors.follow import FollowPlugin
        from GramAddict.core.behaviors.grid_like import GridLikePlugin
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin

        PluginRegistry.reset()
        registry = PluginRegistry()
        registry.register(ProfileGuardPlugin())
        registry.register(FollowPlugin())
        registry.register(GridLikePlugin())

        ctx.context_xml = "<hierarchy>This account is private</hierarchy>"
        ctx.configs.args.follow_percentage = "100"
        ctx.configs.args.likes_percentage = "100"

        results = registry.execute_all(ctx)

        # Only guard should have executed (exclusive)
        assert len(results) == 1
        assert results[0].should_skip is True
        assert results[0].metadata["reason"] == "private"

        PluginRegistry.reset()

    def test_priority_ordering_across_plugins(self):
        from GramAddict.core.behaviors.carousel_browsing import CarouselBrowsingPlugin
        from GramAddict.core.behaviors.follow import FollowPlugin
        from GramAddict.core.behaviors.grid_like import GridLikePlugin
        from GramAddict.core.behaviors.profile_guard import ProfileGuardPlugin
        from GramAddict.core.behaviors.story_view import StoryViewPlugin

        plugins = [
            ProfileGuardPlugin(),
            StoryViewPlugin(),
            FollowPlugin(),
            GridLikePlugin(),
            CarouselBrowsingPlugin(),
        ]

        # Sort by priority descending (registry order)
        plugins.sort(key=lambda p: p.priority, reverse=True)

        order = [p.name for p in plugins]
        assert order == [
            "profile_guard",  # 100
            "follow",  # 60
            "grid_like",  # 50
            "story_view",  # 40
            "carousel_browsing",  # 20
        ]
