"""Security rules for TypeScript/JavaScript MCP server scanning.

Each rule module implements the TSRule base class.
Rules receive raw source code and return list[Finding].
Unlike Python rules which use AST, TS rules use regex pattern matching.
"""

from __future__ import annotations

import re
from abc import ABC, abstractmethod

from ..models import Finding, Severity


class TSRule(ABC):
    """Base class for all TypeScript/JavaScript security rules.

    Uses regex-based pattern matching instead of AST parsing,
    since we avoid external dependencies like tree-sitter.
    """

    @property
    @abstractmethod
    def rule_id(self) -> str:
        """Unique rule identifier (e.g. TS-CMD-001)."""

    @property
    @abstractmethod
    def name(self) -> str:
        """Human-readable rule name."""

    @property
    @abstractmethod
    def severity(self) -> Severity:
        """Default severity level."""

    @property
    @abstractmethod
    def description(self) -> str:
        """Description of what this rule detects."""

    @property
    def cwe(self) -> str:
        """CWE identifier. Override in subclasses."""
        return ""

    @property
    def detailed_description(self) -> str:
        """Extended description for --explain. Falls back to description."""
        return self.description

    @abstractmethod
    def check(self, source: str) -> list[Finding]:
        """Run the rule against raw source code.

        Args:
            source: Raw TypeScript/JavaScript source code string.

        Returns:
            List of findings detected by this rule.
        """

    def _get_lines(self, source: str) -> list[str]:
        """Split source into lines for snippet extraction."""
        return source.splitlines()

    def _get_snippet(self, source: str, lineno: int) -> str:
        """Get a code snippet for the given line number."""
        lines = self._get_lines(source)
        if 1 <= lineno <= len(lines):
            return lines[lineno - 1].strip()
        return ""

    def _is_comment_line(self, line: str) -> bool:
        """Check if a line is a comment (single-line // or block start)."""
        stripped = line.strip()
        return stripped.startswith("//") or stripped.startswith("*")

    def _search_lines(
        self,
        source: str,
        pattern: re.Pattern[str],
        *,
        skip_comments: bool = True,
    ) -> list[tuple[int, str, re.Match[str]]]:
        """Search source lines with a regex pattern.

        Args:
            source: Source code to search.
            pattern: Compiled regex pattern.
            skip_comments: If True, skip comment lines.

        Returns:
            List of (line_number, line_text, match) tuples.
        """
        results: list[tuple[int, str, re.Match[str]]] = []
        for i, line in enumerate(self._get_lines(source), 1):
            if skip_comments and self._is_comment_line(line):
                continue
            m = pattern.search(line)
            if m:
                results.append((i, line, m))
        return results


def get_all_ts_rules() -> list[TSRule]:
    """Return instances of all available TypeScript/JavaScript rules."""
    from .command_injection import TSCommandInjectionRule
    from .sql_injection import TSSqlInjectionRule
    from .path_traversal import TSPathTraversalRule
    from .hardcoded_secrets import TSHardcodedSecretsRule
    from .input_validation import TSMissingInputValidationRule
    from .missing_auth import TSMissingAuthRule
    from .ssrf import TSSSRFRule
    from .unsafe_deser import TSUnsafeDeserializationRule
    from .rate_limiting import TSMissingRateLimitingRule
    from .info_disclosure import TSInfoDisclosureRule
    from .prompt_injection import TSPromptInjectionRule
    from .excessive_perms import TSExcessivePermissionsRule
    from .idor import TSIDORRule
    from .privilege_escalation import TSPrivilegeEscalationRule
    from .missing_tool_authz import TSMissingToolAuthzRule

    return [
        TSCommandInjectionRule(),
        TSSqlInjectionRule(),
        TSPathTraversalRule(),
        TSHardcodedSecretsRule(),
        TSMissingInputValidationRule(),
        TSMissingAuthRule(),
        TSSSRFRule(),
        TSUnsafeDeserializationRule(),
        TSMissingRateLimitingRule(),
        TSInfoDisclosureRule(),
        TSPromptInjectionRule(),
        TSExcessivePermissionsRule(),
        TSIDORRule(),
        TSPrivilegeEscalationRule(),
        TSMissingToolAuthzRule(),
    ]
