"""Detect fixes that suppress symptoms rather than address root causes.

Identifies common anti-patterns like catch-and-ignore, bare except,
and other symptom-suppression patterns that hide errors rather than
fixing them.
"""

from __future__ import annotations

import re
from dataclasses import dataclass
from re import Pattern
from typing import List


@dataclass
class SymptomPattern:
    """A detected symptom-suppression pattern."""

    name: str
    description: str
    severity: str  # "high", "medium", "low"
    match_text: str = ""  # The matched text

    def __str__(self) -> str:
        return f"[{self.severity.upper()}] {self.name}: {self.description}"


@dataclass
class PatternDefinition:
    """Definition of a symptom pattern to detect."""

    name: str
    pattern: str  # Regex pattern
    description: str
    severity: str  # "high", "medium", "low"
    compiled: Pattern = None

    def __post_init__(self):
        """Compile the regex pattern."""
        self.compiled = re.compile(self.pattern, re.MULTILINE | re.IGNORECASE)


class SymptomDetector:
    """Detect fixes that suppress symptoms rather than address root causes.

    Identifies common anti-patterns that indicate a fix is hiding
    errors rather than addressing them properly.
    """

    SYMPTOM_PATTERNS = [
        PatternDefinition(
            name="catch_and_ignore",
            pattern=r"except\s*.*?:\s*(?:pass|\.\.\.)\s*(?:#.*)?$",
            description="Catching exception and ignoring it",
            severity="high",
        ),
        PatternDefinition(
            name="bare_except",
            pattern=r"except\s*:\s*(?!.*(?:raise|log|print|warn))",
            description="Bare except clause without re-raising or logging",
            severity="high",
        ),
        PatternDefinition(
            name="silent_none_return",
            pattern=r"except\s*.*?:\s*return\s+None\s*$",
            description="Returning None on exception hides the error",
            severity="medium",
        ),
        PatternDefinition(
            name="broad_exception_silent",
            pattern=r"except\s+(?:Exception|BaseException)\s*:\s*(?:pass|return\s+None)",
            description="Catching overly broad exception and silencing it",
            severity="high",
        ),
        PatternDefinition(
            name="exception_to_false",
            pattern=r"except\s*.*?:\s*return\s+False\s*$",
            description="Converting exception to False may hide real errors",
            severity="medium",
        ),
        PatternDefinition(
            name="empty_default_get",
            pattern=r"\.get\s*\(\s*[^,)]+\s*\)\s*(?![^#\n]*\b(?:if|or|else|is|!=|==))",
            description="Using .get() without handling None case may hide issues",
            severity="low",
        ),
        PatternDefinition(
            name="or_empty_fallback",
            pattern=r"\w+\s+or\s+(?:\[\]|\{\}|\'\'|\"\"|\(\))",
            description="Using 'or' for empty fallback may hide issues",
            severity="low",
        ),
        PatternDefinition(
            name="hasattr_access",
            pattern=r"hasattr\s*\([^)]+\)\s*and\s+\w+\.\w+",
            description="hasattr + access pattern may hide underlying type issues",
            severity="low",
        ),
        PatternDefinition(
            name="getattr_default_none",
            pattern=r"getattr\s*\(\s*[^,]+,\s*[^,]+,\s*None\s*\)",
            description="getattr with None default may hide missing attributes",
            severity="low",
        ),
        PatternDefinition(
            name="try_except_around_single_line",
            pattern=r"try\s*:\s*\n\s*.{1,50}\n\s*except",
            description="Try/except around single operation may be symptom suppression",
            severity="medium",
        ),
    ]

    def __init__(self, patterns: List[PatternDefinition] = None):
        """Initialize the symptom detector.

        Args:
            patterns: Custom patterns to use (defaults to SYMPTOM_PATTERNS)
        """
        self.patterns = patterns or self.SYMPTOM_PATTERNS

    def detect(self, fix_diff: str) -> List[SymptomPattern]:
        """Detect symptom-suppression patterns in a fix diff.

        Args:
            fix_diff: The unified diff of the fix

        Returns:
            List of detected symptom patterns
        """
        detected = []

        # Extract added lines (without the '+' prefix)
        added_code = self._extract_added_code(fix_diff)

        for pattern_def in self.patterns:
            matches = pattern_def.compiled.findall(added_code)
            for match in matches:
                detected.append(
                    SymptomPattern(
                        name=pattern_def.name,
                        description=pattern_def.description,
                        severity=pattern_def.severity,
                        match_text=match if isinstance(match, str) else match[0],
                    )
                )

        return detected

    def detect_in_code(self, code: str) -> List[SymptomPattern]:
        """Detect symptom-suppression patterns in code.

        Args:
            code: Python source code

        Returns:
            List of detected symptom patterns
        """
        detected = []

        for pattern_def in self.patterns:
            matches = pattern_def.compiled.findall(code)
            for match in matches:
                detected.append(
                    SymptomPattern(
                        name=pattern_def.name,
                        description=pattern_def.description,
                        severity=pattern_def.severity,
                        match_text=match if isinstance(match, str) else match[0],
                    )
                )

        return detected

    def _extract_added_code(self, diff: str) -> str:
        """Extract added lines from a unified diff.

        Args:
            diff: Unified diff string

        Returns:
            String of added code (without '+' prefix)
        """
        lines = []
        for line in diff.split("\n"):
            if line.startswith("+") and not line.startswith("+++"):
                lines.append(line[1:])
        return "\n".join(lines)

    def has_high_severity(self, patterns: List[SymptomPattern]) -> bool:
        """Check if any patterns are high severity.

        Args:
            patterns: List of detected patterns

        Returns:
            True if any high severity patterns found
        """
        return any(p.severity == "high" for p in patterns)

    def format_report(self, patterns: List[SymptomPattern]) -> str:
        """Format detected patterns as a report.

        Args:
            patterns: List of detected patterns

        Returns:
            Formatted report string
        """
        if not patterns:
            return "No symptom-suppression patterns detected."

        lines = [
            "Symptom Suppression Analysis",
            "=" * 40,
            f"Found {len(patterns)} potential issue(s):",
            "",
        ]

        # Group by severity
        by_severity = {"high": [], "medium": [], "low": []}
        for p in patterns:
            by_severity[p.severity].append(p)

        for severity in ["high", "medium", "low"]:
            if by_severity[severity]:
                lines.append(f"## {severity.upper()} Severity ({len(by_severity[severity])})")
                for p in by_severity[severity]:
                    lines.append(f"  - {p.name}: {p.description}")
                lines.append("")

        return "\n".join(lines)
