"""Tests for Teaching Assistant agent and tools."""

import pytest
from unittest.mock import Mock

from agenai.agents.teaching_assistant import (
    TeachingAssistantAgent,
    CodeRunnerTool,
    CodeReviewerTool,
    ExerciseGeneratorTool,
    QuizGeneratorTool,
)
from agenai.agent.llm import LLMResponse


# =============================================================================
# Mock LLM Provider
# =============================================================================

class MockLLMProvider:
    """Mock LLM for testing."""

    def __init__(self, response_text: str = "Mock response"):
        self.response_text = response_text
        self.call_count = 0

    def chat(self, messages, system=None, tools=None, max_tokens=4096, temperature=0.7):
        self.call_count += 1
        return LLMResponse(text=self.response_text, tool_calls=[])


# =============================================================================
# CodeRunnerTool Tests
# =============================================================================

class TestCodeRunnerTool:
    """Tests for CodeRunnerTool."""

    def test_tool_properties(self):
        """Test tool has correct properties."""
        tool = CodeRunnerTool()

        assert tool.name == "run_code"
        assert "Execute" in tool.description
        assert "code" in tool.parameters["properties"]

    def test_execute_simple_code(self):
        """Test executing simple Python code."""
        tool = CodeRunnerTool()

        result = tool.execute(code="print('Hello, World!')")

        assert result.success
        assert "Hello, World!" in result.output

    def test_execute_code_with_error(self):
        """Test executing code that raises an error."""
        tool = CodeRunnerTool()

        result = tool.execute(code="raise ValueError('test error')")

        assert not result.success
        error_text = result.error or result.output
        assert "ValueError" in error_text or "error" in error_text.lower()

    def test_execute_with_timeout(self):
        """Test timeout on long-running code."""
        tool = CodeRunnerTool()

        result = tool.execute(
            code="import time; time.sleep(10)",
            timeout=1,
        )

        assert not result.success
        error_text = result.error or result.output
        assert "timed out" in error_text.lower()


# =============================================================================
# CodeReviewerTool Tests
# =============================================================================

class TestCodeReviewerTool:
    """Tests for CodeReviewerTool."""

    def test_tool_properties(self):
        """Test tool has correct properties."""
        tool = CodeReviewerTool()

        assert tool.name == "review_code"
        assert "code" in tool.parameters["properties"]

    def test_review_simple_code(self):
        """Test reviewing simple code."""
        tool = CodeReviewerTool()

        code = '''
def greet(name):
    """Greet a person."""
    return f"Hello, {name}!"

print(greet("World"))
'''
        result = tool.execute(code=code)

        assert result.success
        assert "Code Review Report" in result.output
        assert "Code Structure" in result.output

    def test_review_code_with_issues(self):
        """Test reviewing code with common issues."""
        tool = CodeReviewerTool()

        code = '''
from os import *
x=1
y=2
try:
    z = x/y
except:
    pass
'''
        result = tool.execute(code=code)

        assert result.success
        assert "Suggestions" in result.output
        # Should catch bare except and import *
        assert "import" in result.output.lower() or "except" in result.output.lower()

    def test_review_with_test_cases(self):
        """Test reviewing code with test cases."""
        tool = CodeReviewerTool()

        code = '''
def add(a, b):
    return a + b

print(add(2, 3))
'''
        test_cases = [
            {"input": "", "expected_output": "5"},
        ]

        result = tool.execute(code=code, test_cases=test_cases)

        assert result.success
        assert "Test Results" in result.output


# =============================================================================
# ExerciseGeneratorTool Tests
# =============================================================================

class TestExerciseGeneratorTool:
    """Tests for ExerciseGeneratorTool."""

    def test_tool_properties(self):
        """Test tool has correct properties."""
        tool = ExerciseGeneratorTool()

        assert tool.name == "create_exercise"
        assert "topic" in tool.parameters["properties"]

    def test_generate_exercise(self):
        """Test generating an exercise."""
        tool = ExerciseGeneratorTool()

        result = tool.execute(
            topic="loops",
            title="Sum of Numbers",
            description="Write a function that calculates the sum of numbers from 1 to n.",
            difficulty="beginner",
        )

        assert result.success
        assert "Exercise" in result.output
        assert "Sum of Numbers" in result.output
        assert "loops" in result.output.lower()

    def test_exercise_includes_starter_code(self):
        """Test that exercise includes starter code."""
        tool = ExerciseGeneratorTool()

        result = tool.execute(
            topic="functions",
            title="Calculate Average",
            description="Write a function to calculate average.",
            include_starter_code=True,
        )

        assert result.success
        assert "Starter Code" in result.output
        assert "def " in result.output

    def test_exercise_includes_hints(self):
        """Test that exercise includes hints."""
        tool = ExerciseGeneratorTool()

        result = tool.execute(
            topic="lists",
            title="Find Maximum",
            description="Find the maximum value.",
            include_hints=True,
        )

        assert result.success
        assert "Hint" in result.output


# =============================================================================
# QuizGeneratorTool Tests
# =============================================================================

class TestQuizGeneratorTool:
    """Tests for QuizGeneratorTool."""

    def test_tool_properties(self):
        """Test tool has correct properties."""
        tool = QuizGeneratorTool()

        assert tool.name == "create_quiz"
        assert "questions" in tool.parameters["properties"]

    def test_generate_quiz(self):
        """Test generating a quiz."""
        tool = QuizGeneratorTool()

        questions = [
            {
                "type": "multiple_choice",
                "question": "What is a variable?",
                "options": ["A container for data", "A function", "A loop", "An error"],
                "correct_answer": "A",
                "explanation": "Variables store data values.",
            },
            {
                "type": "true_false",
                "question": "Python is a compiled language.",
                "correct_answer": "False",
                "explanation": "Python is an interpreted language.",
            },
        ]

        result = tool.execute(
            title="Python Basics",
            topics=["variables", "data types"],
            questions=questions,
        )

        assert result.success
        assert "Quiz" in result.output
        assert "Python Basics" in result.output
        assert "Question 1" in result.output
        assert "Question 2" in result.output
        assert "Answer Key" in result.output


# =============================================================================
# TeachingAssistantAgent Tests
# =============================================================================

class TestTeachingAssistantAgent:
    """Tests for TeachingAssistantAgent."""

    def test_agent_creation(self):
        """Test creating the agent."""
        llm = MockLLMProvider("Test response")
        agent = TeachingAssistantAgent(llm=llm)

        assert agent.name == "teaching_assistant"
        assert "teach" in agent.description.lower() or "assistant" in agent.description.lower()

    def test_agent_has_tools(self):
        """Test that agent has expected tools."""
        llm = MockLLMProvider()
        agent = TeachingAssistantAgent(llm=llm)

        tools = agent.get_tools()
        tool_names = [t.name for t in tools]

        assert "run_code" in tool_names
        assert "review_code" in tool_names
        assert "create_exercise" in tool_names
        assert "create_quiz" in tool_names
        assert "read_file" in tool_names
        assert "write_file" in tool_names

    def test_agent_system_prompt(self):
        """Test that system prompt is teaching-focused."""
        llm = MockLLMProvider()
        agent = TeachingAssistantAgent(llm=llm)

        prompt = agent.get_system_prompt()

        assert "teach" in prompt.lower() or "assistant" in prompt.lower()
        assert "python" in prompt.lower()
        assert "student" in prompt.lower()

    def test_agent_run(self):
        """Test running the agent."""
        llm = MockLLMProvider("Here is an explanation of for loops...")
        agent = TeachingAssistantAgent(llm=llm)

        result = agent.run("Explain for loops")

        assert result.success
        assert result.output == "Here is an explanation of for loops..."

    def test_agent_session_stats(self):
        """Test session statistics tracking."""
        llm = MockLLMProvider("Answer")
        agent = TeachingAssistantAgent(llm=llm)

        # Run a few times
        agent.run("Question 1")
        agent.run("Question 2")

        stats = agent.get_session_stats()

        assert stats["questions_answered"] == 2

    def test_agent_difficulty_level(self):
        """Test setting difficulty level."""
        llm = MockLLMProvider()
        agent = TeachingAssistantAgent(llm=llm, difficulty_level="beginner")

        assert agent.difficulty_level == "beginner"
        assert "beginner" in agent.get_system_prompt().lower()
