"""TS-VAL-001: Missing Input Validation for TypeScript/JavaScript.

Detects MCP tool handlers that process arguments without validation
(no type checks, no schema validation, no zod/joi).
"""

from __future__ import annotations

import re

from . import TSRule
from ..models import Finding, Severity

# MCP tool handler patterns
TOOL_HANDLER_PATTERN = re.compile(
    r'(?:server\.(?:tool|setRequestHandler|handle)|'
    r'\.addTool|\.registerTool|tool\s*\()\s*\('
)
TOOL_DECORATOR_PATTERN = re.compile(
    r'@(?:tool|Tool|handler|Handler)\s*\('
)

# Validation patterns that indicate input is being checked
VALIDATION_PATTERNS = re.compile(
    r'(?:typeof\s+\w+|instanceof\s+|'
    r'\.parse\s*\(|\.validate\s*\(|\.safeParse\s*\(|'
    r'zod\.|Joi\.|z\.\w+|joi\.\w+|'
    r'typeof\s+args|typeof\s+params|typeof\s+input|'
    r'if\s*\(\s*!|if\s*\(\s*typeof|'
    r'schema\.validate|schema\.parse|'
    r'Number\.isFinite|Number\.isInteger|'
    r'Array\.isArray)'
)

# Direct access of args/params without checks
DIRECT_ACCESS = re.compile(
    r'(?:args|params|input|arguments)\s*'
    r'(?:\.\w+|\[\s*[\'"][^\'"]+[\'"]\s*\])'
)

# Function/handler signature patterns
HANDLER_FUNC = re.compile(
    r'(?:async\s+)?(?:function\s+)?(?:handle|tool|process)\w*\s*\('
    r'|(?:handle|tool|process)\w*\s*[:=]\s*(?:async\s+)?'
    r'(?:\([^)]*\)|[a-zA-Z_]\w*)\s*=>'
)


class TSMissingInputValidationRule(TSRule):
    """Detects missing input validation in MCP tool handlers."""

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

    @property
    def name(self) -> str:
        return "Missing Input Validation"

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

    @property
    def cwe(self) -> str:
        return "CWE-20"

    @property
    def description(self) -> str:
        return "MCP tool handler processes arguments without validation."

    @property
    def detailed_description(self) -> str:
        return (
            "MCP tool handler processes arguments without validation.\n\n"
            "Tool handlers in MCP servers receive arguments from LLM\n"
            "interactions. Without validation, malformed or malicious\n"
            "input can cause unexpected behavior.\n\n"
            "Examples of vulnerable code:\n"
            "  server.tool('read', async (args) => {\n"
            "    return fs.readFile(args.path);\n"
            "  });\n\n"
            "How to fix:\n"
            "  - Use Zod schemas for input validation\n"
            "  - Add typeof/instanceof checks\n"
            "  - Validate with JSON Schema\n"
            "  - Use the MCP SDK's built-in schema parameter"
        )

    def check(self, source: str) -> list[Finding]:
        """Check for missing input validation in handlers."""
        findings: list[Finding] = []
        lines = self._get_lines(source)
        has_validation_import = bool(re.search(
            r'''(?:from\s+['"]zod['"]|require\s*\(['"]zod['"]\)|'''
            r'''from\s+['"]joi['"]|require\s*\(['"]joi['"]\))''',
            source
        ))

        # Find tool handler registrations
        for i, line in enumerate(lines, 1):
            if self._is_comment_line(line):
                continue

            is_handler = (
                TOOL_HANDLER_PATTERN.search(line)
                or HANDLER_FUNC.search(line)
            )
            if not is_handler:
                continue

            # Look ahead for validation in the handler body
            body_end = min(i + 20, len(lines))
            body = "\n".join(lines[i - 1:body_end])

            has_validation = (
                has_validation_import
                or bool(VALIDATION_PATTERNS.search(body))
            )

            if not has_validation and DIRECT_ACCESS.search(body):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=self.severity,
                    title="Missing Input Validation",
                    description=(
                        "Tool handler accesses arguments without "
                        "type checking or schema validation."
                    ),
                    file="",
                    line=i,
                    code_snippet=line.strip()[:80],
                    recommendation=(
                        "Add Zod schema validation or typeof checks "
                        "before accessing arguments."
                    ),
                    cwe=self.cwe,
                ))

        return findings
