"""TS-INFO-001: Information Disclosure for TypeScript/JavaScript.

Detects patterns that expose stack traces, verbose errors,
or internal information to clients.
"""

from __future__ import annotations

import re

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

# Stack trace exposure patterns
STACK_TRACE_RETURN = re.compile(
    r'(?:res\.(?:send|json|write)|return)\s*\([^)]*'
    r'(?:\.stack|\.message|error\.toString|err\.toString)'
)
CATCH_EXPOSE = re.compile(
    r'catch\s*\(\s*(\w+)\s*\)'
)
ERROR_IN_RESPONSE = re.compile(
    r'(?:res\.(?:send|json|status\s*\(\s*\d+\s*\)\.(?:send|json)))\s*\(\s*'
    r'(?:err\b|error\b|e\.message|e\.stack)'
)
VERBOSE_ERROR = re.compile(
    r'(?:stack|stackTrace|trace|debug[_-]?info|internal[_-]?error)\s*:'
    r'\s*(?:true|err|error)',
    re.IGNORECASE
)
CONSOLE_ERROR_SENSITIVE = re.compile(
    r'console\.(?:log|error|warn)\s*\([^)]*'
    r'(?:password|secret|token|key|credential)',
    re.IGNORECASE
)


class TSInfoDisclosureRule(TSRule):
    """Detects information disclosure patterns."""

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

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

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

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

    @property
    def description(self) -> str:
        return (
            "Stack traces or verbose error details exposed to clients."
        )

    @property
    def detailed_description(self) -> str:
        return (
            "Stack traces or verbose error details exposed to clients.\n\n"
            "Exposing internal error details can reveal information\n"
            "about the server's internals to attackers.\n\n"
            "Examples of vulnerable code:\n"
            "  catch (e) { res.send(e.stack); }\n"
            "  return { error: err.message, stack: err.stack };\n\n"
            "How to fix:\n"
            "  - Return generic error messages to clients\n"
            "  - Log detailed errors server-side only\n"
            "  - Use error IDs for client correlation"
        )

    def check(self, source: str) -> list[Finding]:
        """Check for information disclosure patterns."""
        findings: list[Finding] = []
        lines = self._get_lines(source)

        for i, line in enumerate(lines, 1):
            if self._is_comment_line(line):
                continue

            # Stack trace in response
            if STACK_TRACE_RETURN.search(line):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=Severity.MEDIUM,
                    title="Stack Trace Exposure",
                    description=(
                        "Error stack trace returned in response."
                    ),
                    file="",
                    line=i,
                    code_snippet=line.strip()[:80],
                    recommendation=(
                        "Return generic error messages. Log "
                        "details server-side only."
                    ),
                    cwe=self.cwe,
                ))
                continue

            # Error details in response
            if ERROR_IN_RESPONSE.search(line):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=self.severity,
                    title="Error Details in Response",
                    description=(
                        "Error object sent in HTTP response."
                    ),
                    file="",
                    line=i,
                    code_snippet=line.strip()[:80],
                    recommendation=(
                        "Return a generic error message instead "
                        "of the raw error object."
                    ),
                    cwe=self.cwe,
                ))
                continue

            # Console logging of sensitive data
            if CONSOLE_ERROR_SENSITIVE.search(line):
                findings.append(Finding(
                    rule_id=self.rule_id,
                    severity=Severity.MEDIUM,
                    title="Sensitive Data in Logs",
                    description=(
                        "Potentially sensitive data logged to console."
                    ),
                    file="",
                    line=i,
                    code_snippet=line.strip()[:80],
                    recommendation=(
                        "Redact sensitive values before logging."
                    ),
                    cwe="CWE-532",
                ))

        return findings
