"""Tests for dogfood-identified fixes.

Fix 1: ThoughtNode.get_path() cycle guard
Fix 2: Tool-aware mode prompts (scan/debug/fix/chat)
Fix 3: Intent classifier (detect_mode)
Fix 4: Denser turns (base prompt changes)
Fix 5: TodoWrite exclusion
"""

from __future__ import annotations

import pytest


# ============================================================================
# Fix 1: ThoughtNode.get_path() Cycle Guard
# ============================================================================


class TestThoughtNodeCycleGuard:
    """S-10: get_path() must not infinite loop on cyclic parent pointers."""

    def test_normal_path(self):
        """Linear chain returns correct root-to-leaf path."""
        from autodebug.llm_first.models import ThoughtNode

        root = ThoughtNode(thought="root", depth=0)
        child = ThoughtNode(thought="child", depth=1, parent=root)
        leaf = ThoughtNode(thought="leaf", depth=2, parent=child)
        root.children = [child]
        child.children = [leaf]

        path = leaf.get_path()
        assert len(path) == 3
        assert path[0].thought == "root"
        assert path[1].thought == "child"
        assert path[2].thought == "leaf"

    def test_single_node(self):
        """Single node with no parent returns [self]."""
        from autodebug.llm_first.models import ThoughtNode

        node = ThoughtNode(thought="solo", depth=0)
        path = node.get_path()
        assert len(path) == 1
        assert path[0].thought == "solo"

    def test_cycle_breaks_cleanly(self):
        """Cyclic parent pointer terminates instead of infinite looping."""
        from autodebug.llm_first.models import ThoughtNode

        a = ThoughtNode(thought="a", depth=0)
        b = ThoughtNode(thought="b", depth=1, parent=a)
        c = ThoughtNode(thought="c", depth=2, parent=b)
        # Create cycle: a -> b -> c -> a
        a.parent = c

        path = c.get_path()
        # Should terminate. Exact length depends on where cycle is detected,
        # but must be finite and contain no more than 3 unique nodes.
        assert len(path) <= 4
        thoughts = [n.thought for n in path]
        assert "c" in thoughts

    def test_self_referencing_node(self):
        """Node whose parent is itself terminates."""
        from autodebug.llm_first.models import ThoughtNode

        node = ThoughtNode(thought="loop", depth=0)
        node.parent = node

        path = node.get_path()
        assert len(path) == 1
        assert path[0].thought == "loop"

    def test_source_has_seen_set(self):
        """Verify get_path source code contains cycle detection."""
        import inspect
        from autodebug.llm_first.models import ThoughtNode

        source = inspect.getsource(ThoughtNode.get_path)
        assert "seen" in source, "get_path must use a 'seen' set for cycle detection"


# ============================================================================
# Fix 2: Tool-Aware Mode Prompts
# ============================================================================


class TestModePrompts:
    """Verify mode prompt architecture and content."""

    def test_base_prompt_exists(self):
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "Hikaflow" in HIKAFLOW_BASE_PROMPT
        assert "Security" in HIKAFLOW_BASE_PROMPT

    def test_scan_prompt_has_tool_recipes(self):
        from autodebug.agent.system_prompt import HIKAFLOW_SCAN_PROMPT
        assert "scan_codebase" in HIKAFLOW_SCAN_PROMPT
        assert "review_files" in HIKAFLOW_SCAN_PROMPT
        assert "mode=" in HIKAFLOW_SCAN_PROMPT

    def test_debug_prompt_has_tool_recipes(self):
        from autodebug.agent.system_prompt import HIKAFLOW_DEBUG_PROMPT
        assert "get_error_details" in HIKAFLOW_DEBUG_PROMPT
        assert "failure_similarity_search" in HIKAFLOW_DEBUG_PROMPT
        assert "generate_fix_candidates" in HIKAFLOW_DEBUG_PROMPT

    def test_fix_prompt_has_tool_recipes(self):
        from autodebug.agent.system_prompt import HIKAFLOW_FIX_PROMPT
        assert "generate_fix_candidates" in HIKAFLOW_FIX_PROMPT
        assert "validate_fix_candidate" in HIKAFLOW_FIX_PROMPT
        assert "create_fix_pr" in HIKAFLOW_FIX_PROMPT
        assert "dry_run=True" in HIKAFLOW_FIX_PROMPT

    def test_chat_prompt_allows_text_only(self):
        from autodebug.agent.system_prompt import HIKAFLOW_CHAT_PROMPT
        assert "optional" in HIKAFLOW_CHAT_PROMPT.lower()

    def test_mode_prompts_registry_complete(self):
        from autodebug.agent.system_prompt import MODE_PROMPTS
        assert set(MODE_PROMPTS.keys()) == {"scan", "debug", "fix", "chat"}

    def test_backward_compat_composite(self):
        """HIKAFLOW_SYSTEM_PROMPT is base + debug for backward compat."""
        from autodebug.agent.system_prompt import (
            HIKAFLOW_SYSTEM_PROMPT,
            HIKAFLOW_BASE_PROMPT,
            HIKAFLOW_DEBUG_PROMPT,
        )
        assert "Hikaflow" in HIKAFLOW_SYSTEM_PROMPT
        assert "{skill_menu}" in HIKAFLOW_SYSTEM_PROMPT
        # Debug mode content should be present
        assert "Root cause" in HIKAFLOW_SYSTEM_PROMPT
        assert "Coverage" in HIKAFLOW_SYSTEM_PROMPT

    def test_base_prompt_no_todowrite(self):
        """Base prompt explicitly forbids TodoWrite."""
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "TodoWrite" in HIKAFLOW_BASE_PROMPT
        assert "Do NOT" in HIKAFLOW_BASE_PROMPT

    def test_scan_prompt_structured_output(self):
        """Scan mode requires structured table output."""
        from autodebug.agent.system_prompt import HIKAFLOW_SCAN_PROMPT
        assert "Severity" in HIKAFLOW_SCAN_PROMPT
        assert "File:Line" in HIKAFLOW_SCAN_PROMPT
        assert "Proposed Fix" in HIKAFLOW_SCAN_PROMPT

    def test_scan_prompt_turn_target(self):
        """Scan mode has turn count guidance."""
        from autodebug.agent.system_prompt import HIKAFLOW_SCAN_PROMPT
        assert "4-6 turns" in HIKAFLOW_SCAN_PROMPT

    def test_base_prompt_batch_tools(self):
        """Base prompt instructs batching independent tool calls."""
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "Batch" in HIKAFLOW_BASE_PROMPT or "batch" in HIKAFLOW_BASE_PROMPT

    def test_base_prompt_first_turn_only(self):
        """Base prompt limits plan narration to first turn."""
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "FIRST turn" in HIKAFLOW_BASE_PROMPT

    def test_base_prompt_session_state_at_end(self):
        """Base prompt says update session_state at END, not during."""
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "END" in HIKAFLOW_BASE_PROMPT
        assert "not during" in HIKAFLOW_BASE_PROMPT


# ============================================================================
# Fix 3: Intent Classifier
# ============================================================================


class TestDetectMode:
    """Verify intent classifier correctly routes to modes."""

    def test_scan_triggers(self):
        from autodebug.agent.core import detect_mode
        assert detect_mode("scan the codebase for bugs") == "scan"
        assert detect_mode("find issues in the code") == "scan"
        assert detect_mode("check for security vulnerabilities") == "scan"
        assert detect_mode("review codebase") == "scan"
        assert detect_mode("security audit") == "scan"
        assert detect_mode("find bugs") == "scan"

    def test_debug_triggers(self):
        from autodebug.agent.core import detect_mode
        assert detect_mode("debug test_login") == "debug"
        assert detect_mode("why is this failing") == "debug"
        assert detect_mode("investigate the error") == "debug"
        assert detect_mode("I got a traceback") == "debug"
        assert detect_mode("what's wrong with test_api") == "debug"

    def test_fix_triggers(self):
        from autodebug.agent.core import detect_mode
        assert detect_mode("fix this bug") == "fix"
        assert detect_mode("fix the flaky test") == "fix"
        assert detect_mode("heal test_login") == "fix"
        assert detect_mode("create pr for the fix") == "fix"
        assert detect_mode("apply fix to models.py") == "fix"

    def test_chat_default(self):
        from autodebug.agent.core import detect_mode
        assert detect_mode("hello") == "chat"
        assert detect_mode("what does this function do") == "chat"
        assert detect_mode("explain the architecture") == "chat"
        assert detect_mode("") == "chat"

    def test_fix_takes_priority_over_debug(self):
        """'fix this failing test' should be fix, not debug."""
        from autodebug.agent.core import detect_mode
        assert detect_mode("fix this failing test") == "fix"

    def test_case_insensitive(self):
        from autodebug.agent.core import detect_mode
        assert detect_mode("SCAN THE CODEBASE") == "scan"
        assert detect_mode("FIX THIS") == "fix"


# ============================================================================
# Fix 3b: build_system_prompt mode integration
# ============================================================================


class TestBuildSystemPromptModes:
    """Verify build_system_prompt selects correct mode prompt."""

    def test_scan_mode_selected(self):
        from autodebug.agent.core import build_system_prompt
        prompt = build_system_prompt(user_message="scan the codebase for bugs")
        assert "Mode: Scan" in prompt
        assert "scan_codebase" in prompt

    def test_debug_mode_selected(self):
        from autodebug.agent.core import build_system_prompt
        prompt = build_system_prompt(user_message="debug test_login")
        assert "Mode: Debug" in prompt
        assert "get_error_details" in prompt

    def test_fix_mode_selected(self):
        from autodebug.agent.core import build_system_prompt
        prompt = build_system_prompt(user_message="fix the flaky test")
        assert "Mode: Fix" in prompt
        assert "validate_fix_candidate" in prompt

    def test_chat_mode_selected(self):
        from autodebug.agent.core import build_system_prompt
        prompt = build_system_prompt(user_message="hello there")
        assert "Mode: Chat" in prompt

    def test_default_is_debug(self):
        """No user_message defaults to debug mode (backward compat)."""
        from autodebug.agent.core import build_system_prompt
        prompt = build_system_prompt()
        assert "Mode: Debug" in prompt

    def test_base_always_included(self):
        """Base prompt content present regardless of mode."""
        from autodebug.agent.core import build_system_prompt
        for msg in ["scan for bugs", "debug test", "fix it", "hello"]:
            prompt = build_system_prompt(user_message=msg)
            assert "Hikaflow" in prompt
            assert "Security" in prompt
            assert "Shared Rules" in prompt


# ============================================================================
# Fix 5: TodoWrite Exclusion
# ============================================================================


class TestTodoWriteExclusion:
    """Verify TodoWrite is not in the allowed tools list."""

    def test_allowed_tools_no_todowrite(self):
        """The allowed_tools list should not include TodoWrite."""
        import inspect
        from autodebug.agent import core
        source = inspect.getsource(core._create_claude_sdk_agent)
        assert "TodoWrite" not in source
        assert "TaskCreate" not in source

    def test_prompt_forbids_todowrite(self):
        """System prompt explicitly says not to use TodoWrite."""
        from autodebug.agent.system_prompt import HIKAFLOW_BASE_PROMPT
        assert "TodoWrite" in HIKAFLOW_BASE_PROMPT


# ============================================================================
# Worker Prompts (existing workers still work)
# ============================================================================


class TestWorkerPromptsIntact:
    """Verify worker prompts weren't broken by mode prompt changes."""

    def test_security_worker_formattable(self):
        from autodebug.agent.system_prompt import SECURITY_WORKER_PROMPT
        formatted = SECURITY_WORKER_PROMPT.format(project_context="test ctx")
        assert "test ctx" in formatted
        assert "injection" in formatted.lower()

    def test_async_worker_formattable(self):
        from autodebug.agent.system_prompt import ASYNC_WORKER_PROMPT
        formatted = ASYNC_WORKER_PROMPT.format(project_context="test ctx")
        assert "test ctx" in formatted
        assert "race condition" in formatted.lower()

    def test_code_quality_worker_formattable(self):
        from autodebug.agent.system_prompt import CODE_QUALITY_WORKER_PROMPT
        formatted = CODE_QUALITY_WORKER_PROMPT.format(project_context="test ctx")
        assert "test ctx" in formatted
        assert "resource leak" in formatted.lower()

    def test_review_worker_formattable(self):
        from autodebug.agent.system_prompt import REVIEW_WORKER_PROMPT
        formatted = REVIEW_WORKER_PROMPT.format(project_context="test ctx")
        assert "test ctx" in formatted
