"""Code reviewer tool - Analyze and grade student code."""

from __future__ import annotations

import subprocess
import tempfile
import os
import re
from typing import Any

from agenai.agent.tools.base import Tool, ToolResult


class CodeReviewerTool(Tool):
    """Review student code and provide structured feedback."""

    @property
    def name(self) -> str:
        return "review_code"

    @property
    def description(self) -> str:
        return """Review student Python code and provide detailed feedback.

Analyzes:
- Code correctness (runs without errors)
- Code structure (functions, classes, organization)
- Style (comments, naming, readability)
- Test case results (if provided)

Returns a structured review report."""

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "code": {
                    "type": "string",
                    "description": "Student's Python code to review",
                },
                "test_cases": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "input": {"type": "string"},
                            "expected_output": {"type": "string"},
                        },
                    },
                    "description": "Test cases to run against the code",
                },
                "requirements": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Specific requirements to check for",
                },
            },
            "required": ["code"],
        }

    def execute(
        self,
        code: str,
        test_cases: list[dict] | None = None,
        requirements: list[str] | None = None,
    ) -> ToolResult:
        """Review the code and return feedback."""
        try:
            report = []
            report.append("# Code Review Report\n")

            # 1. Static Analysis
            static_analysis = self._analyze_structure(code)
            report.append("## Code Structure")
            report.append(f"- Lines of code: {static_analysis['line_count']}")
            report.append(f"- Functions defined: {static_analysis['function_count']}")
            report.append(f"- Classes defined: {static_analysis['class_count']}")
            report.append(f"- Has comments: {'Yes' if static_analysis['has_comments'] else 'No'}")
            report.append(f"- Has docstrings: {'Yes' if static_analysis['has_docstrings'] else 'No'}")
            report.append("")

            # 2. Execution Test
            exec_result = self._test_execution(code)
            report.append("## Execution Test")
            if exec_result["success"]:
                report.append("Code executes without errors.")
                if exec_result["output"]:
                    report.append(f"Output:\n```\n{exec_result['output']}\n```")
            else:
                report.append("Code has errors:")
                report.append(f"```\n{exec_result['error']}\n```")
            report.append("")

            # 3. Test Cases
            if test_cases:
                report.append("## Test Results")
                test_results = self._run_test_cases(code, test_cases)
                passed = sum(1 for t in test_results if t["passed"])
                report.append(f"Passed: {passed}/{len(test_results)}")
                for i, result in enumerate(test_results, 1):
                    status = "PASS" if result["passed"] else "FAIL"
                    report.append(f"- Test {i}: {status}")
                    if not result["passed"]:
                        report.append(f"  Expected: {result['expected']}")
                        report.append(f"  Got: {result['actual']}")
                report.append("")

            # 4. Style Suggestions
            suggestions = self._get_suggestions(code, static_analysis)
            if suggestions:
                report.append("## Suggestions for Improvement")
                for suggestion in suggestions:
                    report.append(f"- {suggestion}")
                report.append("")

            # 5. Requirements Check
            if requirements:
                report.append("## Requirements Check")
                for req in requirements:
                    # Simple keyword check
                    met = req.lower() in code.lower()
                    status = "Met" if met else "Not found"
                    report.append(f"- {req}: {status}")

            return ToolResult.ok("\n".join(report))

        except Exception as e:
            return ToolResult.fail(f"Error reviewing code: {e}")

    def _analyze_structure(self, code: str) -> dict:
        """Analyze code structure."""
        lines = code.splitlines()
        return {
            "line_count": len(lines),
            "function_count": len(re.findall(r"^\s*def\s+\w+", code, re.MULTILINE)),
            "class_count": len(re.findall(r"^\s*class\s+\w+", code, re.MULTILINE)),
            "has_comments": "#" in code,
            "has_docstrings": '"""' in code or "'''" in code,
        }

    def _test_execution(self, code: str) -> dict:
        """Test if code executes without errors."""
        try:
            with tempfile.NamedTemporaryFile(
                mode="w", suffix=".py", delete=False
            ) as f:
                f.write(code)
                temp_path = f.name

            try:
                result = subprocess.run(
                    ["python", temp_path],
                    capture_output=True,
                    text=True,
                    timeout=5,
                )
                return {
                    "success": result.returncode == 0,
                    "output": result.stdout.strip() if result.stdout else "",
                    "error": result.stderr.strip() if result.stderr else "",
                }
            finally:
                os.unlink(temp_path)

        except subprocess.TimeoutExpired:
            return {"success": False, "output": "", "error": "Execution timed out"}
        except Exception as e:
            return {"success": False, "output": "", "error": str(e)}

    def _run_test_cases(self, code: str, test_cases: list[dict]) -> list[dict]:
        """Run test cases against the code."""
        results = []

        for test in test_cases:
            test_input = test.get("input", "")
            expected = test.get("expected_output", "").strip()

            try:
                with tempfile.NamedTemporaryFile(
                    mode="w", suffix=".py", delete=False
                ) as f:
                    f.write(code)
                    temp_path = f.name

                try:
                    result = subprocess.run(
                        ["python", temp_path],
                        capture_output=True,
                        text=True,
                        timeout=5,
                        input=test_input,
                    )
                    actual = result.stdout.strip()
                    passed = actual == expected

                    results.append({
                        "passed": passed,
                        "expected": expected,
                        "actual": actual,
                    })
                finally:
                    os.unlink(temp_path)

            except Exception as e:
                results.append({
                    "passed": False,
                    "expected": expected,
                    "actual": f"Error: {e}",
                })

        return results

    def _get_suggestions(self, code: str, analysis: dict) -> list[str]:
        """Generate improvement suggestions."""
        suggestions = []

        if not analysis["has_comments"]:
            suggestions.append(
                "Add comments to explain complex logic or the purpose of your code"
            )

        if analysis["function_count"] == 0 and analysis["line_count"] > 10:
            suggestions.append(
                "Consider breaking your code into functions for better organization"
            )

        if analysis["has_docstrings"] is False and analysis["function_count"] > 0:
            suggestions.append(
                "Add docstrings to your functions to document what they do"
            )

        # Check for common issues
        if "import *" in code:
            suggestions.append(
                "Avoid 'from module import *' - import specific items instead"
            )

        if re.search(r"except\s*:", code):
            suggestions.append(
                "Avoid bare 'except:' - catch specific exceptions instead"
            )

        return suggestions
