"""
🔴 RED TDD: Step-Aware Navigation Validation + Topology Guard

Tests that validate the GOAP planner correctly handles multi-step navigation:
- Intermediate steps (tap profile tab → OWN_PROFILE) must be ACCEPTED
- Wrong screens (tap profile tab → REELS_FEED) must be REJECTED
- Structural HD Map actions must NEVER be aversively learned as traps
"""

import os
import sys
from unittest.mock import MagicMock

import pytest

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../")))

from GramAddict.core.goap import GoalExecutor, GoalPlanner, ScreenType
from GramAddict.core.screen_topology import ScreenTopology


class TestStepAwareValidation:
    """_execute_action must validate intermediate steps, not just final goals."""

    @pytest.fixture
    def executor(self):
        device = MagicMock()
        device.dump_hierarchy.return_value = "<xml>mock</xml>"
        device.click = MagicMock()
        executor = GoalExecutor(device, bot_username="testbot")
        executor.action_failures = {}
        return executor

    def test_intermediate_step_accepted(self, executor):
        """
        Action: 'tap profile tab' from HOME_FEED
        Goal: 'open following list'
        Landing: OWN_PROFILE (intermediate step)

        The validator must ACCEPT this — OWN_PROFILE is the correct
        intermediate screen for 'tap profile tab' from HOME_FEED.
        It must NOT reject it because the goal says 'following'.
        """
        # The critical question: does expected_screen_for_action return OWN_PROFILE?
        expected = ScreenTopology.expected_screen_for_action("tap profile tab", ScreenType.HOME_FEED)
        assert expected == ScreenType.OWN_PROFILE, "HD Map must know 'tap profile tab' → OWN_PROFILE"

        # And the goal target is FOLLOW_LIST, which is DIFFERENT from where we landed
        goal_target = ScreenTopology.goal_to_target_screen("open following list")
        assert goal_target == ScreenType.FOLLOW_LIST

        # The old code would fail here because it checks goal_target against landing screen.
        # The new code checks expected (from action) against landing screen.
        landing = ScreenType.OWN_PROFILE
        assert landing == expected, "Step validation: landing matches expected → ACCEPT"
        assert landing != goal_target, "Goal not yet achieved — but step is valid"

    def test_wrong_screen_rejected(self, executor):
        """
        Action: 'tap profile tab' from HOME_FEED
        Landing: REELS_FEED (wrong!)

        The validator must REJECT this — expected was OWN_PROFILE.
        """
        expected = ScreenTopology.expected_screen_for_action("tap profile tab", ScreenType.HOME_FEED)
        landing = ScreenType.REELS_FEED
        assert landing != expected, "Wrong screen: REELS_FEED ≠ OWN_PROFILE → REJECT"

    def test_final_step_accepted(self, executor):
        """
        Action: 'tap following list' from OWN_PROFILE
        Goal: 'open following list'
        Landing: FOLLOW_LIST (final step!)

        The validator must ACCEPT this.
        """
        expected = ScreenTopology.expected_screen_for_action("tap following list", ScreenType.OWN_PROFILE)
        assert expected == ScreenType.FOLLOW_LIST
        landing = ScreenType.FOLLOW_LIST
        assert landing == expected


class TestTopologyGuard:
    """Structural HD Map actions must NEVER be aversively learned as traps."""

    def test_structural_action_not_burned(self):
        """'tap profile tab' on HOME_FEED is structural — must not be burned."""
        assert ScreenTopology.is_structural_action(ScreenType.HOME_FEED, "tap profile tab") is True

    def test_non_structural_action_can_be_burned(self):
        """'open following list' on HOME_FEED is NOT structural — can be burned."""
        assert ScreenTopology.is_structural_action(ScreenType.HOME_FEED, "open following list") is False


class TestSSOTConsolidation:
    """All goal→screen mappings must use ScreenTopology as SSOT."""

    def test_is_goal_achieved_uses_topology_for_following(self):
        """_is_goal_achieved must recognize FOLLOW_LIST for 'open following list'."""
        planner = GoalPlanner("testbot")
        planner.knowledge = MagicMock()
        planner.knowledge.is_trap.return_value = False
        planner.knowledge.get_requirements.return_value = None

        # On FOLLOW_LIST with goal "open following list" → achieved
        result = planner._is_goal_achieved("open following list", ScreenType.FOLLOW_LIST, {})
        assert result is True, "Goal must be achieved when on target screen"

    def test_is_goal_achieved_not_achieved_on_wrong_screen(self):
        planner = GoalPlanner("testbot")
        result = planner._is_goal_achieved("open following list", ScreenType.HOME_FEED, {})
        assert result is False

    def test_navigate_to_screen_uses_topology(self):
        """navigate_to_screen must use ScreenTopology.screen_name_to_goal()."""
        assert ScreenTopology.screen_name_to_goal("FollowingList") == "open following list"
        assert ScreenTopology.screen_name_to_goal("ExploreFeed") == "open explore feed"
        assert ScreenTopology.screen_name_to_goal("OwnProfile") == "open profile"
