"""ERR-001: Unhandled Exceptions in Tool Functions detection rule."""

from __future__ import annotations

import ast

from . import Rule
from ..models import Finding, Severity

TOOL_NAME_KEYWORDS = {"tool", "handle"}


def _has_tool_decorator(node: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
    for dec in node.decorator_list:
        if isinstance(dec, ast.Attribute) and dec.attr == "tool":
            return True
        if isinstance(dec, ast.Name) and dec.id == "tool":
            return True
        if isinstance(dec, ast.Call):
            if isinstance(dec.func, ast.Attribute) and dec.func.attr == "tool":
                return True
            if isinstance(dec.func, ast.Name) and dec.func.id == "tool":
                return True
    return False


def _has_try_except(body: list[ast.stmt]) -> bool:
    for stmt in body:
        if isinstance(stmt, ast.Try):
            return True
    return False


def _is_tool_function(node: ast.FunctionDef | ast.AsyncFunctionDef) -> bool:
    if _has_tool_decorator(node):
        return True
    name_lower = node.name.lower()
    return any(kw in name_lower for kw in TOOL_NAME_KEYWORDS)


class UnhandledToolErrorRule(Rule):
    """Detects MCP tool handlers without try/except blocks."""

    @property
    def rule_id(self) -> str:
        return "ERR-001"

    @property
    def name(self) -> str:
        return "Unhandled Exceptions in Tool Functions"

    @property
    def severity(self) -> Severity:
        return Severity.MEDIUM

    @property
    def description(self) -> str:
        return "Tool handler without try/except may leak unstructured errors."

    def check(self, tree: ast.Module, source: str) -> list[Finding]:
        findings: list[Finding] = []
        for node in ast.walk(tree):
            if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                continue
            if not _is_tool_function(node):
                continue
            if not _has_try_except(node.body):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=self.severity,
                    title="Tool handler without error handling",
                    description=f"Function '{node.name}' lacks try/except block.",
                    file="",
                    line=node.lineno,
                    code_snippet=self._get_snippet(source, node.lineno),
                    recommendation="Wrap tool implementations in try/except to return structured errors.",
                    cwe="CWE-755",
                    confidence="MEDIUM",
                ))
        return findings
