#!/usr/bin/env python3
"""
Design Pattern Recognition Module

Detects common design patterns in codebases across multiple languages.

Supported Patterns:
- Creational: Singleton, Factory, Builder, Prototype
- Structural: Adapter, Decorator, Facade, Proxy
- Behavioral: Observer, Strategy, Command, Template Method, Chain of Responsibility

Detection Levels:
- Surface: Naming conventions (e.g., "Factory", "Singleton")
- Deep: Structural analysis (class relationships, method signatures)
- Full: Behavioral analysis (method interactions, state management)

Credits:
- Design pattern definitions: Gang of Four (GoF) Design Patterns
- Detection heuristics: Inspired by academic research on pattern mining
"""

import argparse
import json
import logging
import sys
from dataclasses import dataclass, field
from pathlib import Path

logger = logging.getLogger(__name__)

# Confidence thresholds for pattern filtering (Issue #240)
CONFIDENCE_THRESHOLDS = {
    "critical": 0.80,  # High-confidence patterns for ARCHITECTURE.md
    "high": 0.70,  # Include in detailed analysis
    "medium": 0.60,  # Include with warning/context
    "low": 0.50,  # Minimum detection threshold
}

# Default minimum confidence for pattern detection
DEFAULT_MIN_CONFIDENCE = CONFIDENCE_THRESHOLDS["low"]


@dataclass
class PatternInstance:
    """Single detected pattern instance"""

    pattern_type: str  # e.g., 'Singleton', 'Factory'
    category: str  # 'Creational', 'Structural', 'Behavioral'
    confidence: float  # 0.0-1.0
    location: str  # File path
    class_name: str | None = None
    method_name: str | None = None
    line_number: int | None = None
    evidence: list[str] = field(default_factory=list)  # Evidence for detection
    related_classes: list[str] = field(default_factory=list)  # Related pattern classes
    ai_analysis: dict | None = None  # AI-generated analysis (C3.6)

    def to_dict(self) -> dict:
        """Export to dictionary"""
        result = {
            "pattern_type": self.pattern_type,
            "category": self.category,
            "confidence": self.confidence,
            "location": self.location,
            "class_name": self.class_name,
            "method_name": self.method_name,
            "line_number": self.line_number,
            "evidence": self.evidence,
            "related_classes": self.related_classes,
        }
        if self.ai_analysis:
            result["ai_analysis"] = self.ai_analysis
        return result


@dataclass
class PatternReport:
    """Complete pattern detection report"""

    file_path: str
    language: str
    patterns: list[PatternInstance]
    total_classes: int
    total_functions: int
    analysis_depth: str  # 'surface', 'deep', 'full'

    def to_dict(self) -> dict:
        """Export to dictionary"""
        return {
            "file_path": self.file_path,
            "language": self.language,
            "patterns": [p.to_dict() for p in self.patterns],
            "total_classes": self.total_classes,
            "total_functions": self.total_functions,
            "analysis_depth": self.analysis_depth,
            "pattern_summary": self.get_summary(),
        }

    def get_summary(self) -> dict[str, int]:
        """Get pattern count summary"""
        summary = {}
        for pattern in self.patterns:
            summary[pattern.pattern_type] = summary.get(pattern.pattern_type, 0) + 1
        return summary


class BasePatternDetector:
    """Base class for all pattern detectors"""

    def __init__(self, depth: str = "deep"):
        """
        Initialize detector.

        Args:
            depth: Detection depth ('surface', 'deep', 'full')
        """
        self.depth = depth
        self.pattern_type = "BasePattern"
        self.category = "Unknown"

    def detect_surface(self, _class_sig, _all_classes: list) -> PatternInstance | None:
        """
        Surface-level detection using naming conventions.

        Args:
            class_sig: Class signature to analyze
            all_classes: All classes in the file for context

        Returns:
            PatternInstance if pattern detected, None otherwise
        """
        # Default: no surface detection
        return None

    def detect_deep(self, _class_sig, _all_classes: list) -> PatternInstance | None:
        """
        Deep detection using structural analysis.

        Args:
            class_sig: Class signature to analyze
            all_classes: All classes in the file for context

        Returns:
            PatternInstance if pattern detected, None otherwise
        """
        # Default: no deep detection
        return None

    def detect_full(
        self, _class_sig, _all_classes: list, _file_content: str
    ) -> PatternInstance | None:
        """
        Full detection using behavioral analysis.

        Args:
            class_sig: Class signature to analyze
            all_classes: All classes in the file for context
            file_content: Full file content for advanced analysis

        Returns:
            PatternInstance if pattern detected, None otherwise
        """
        # Default: no full detection
        return None

    def detect(
        self, class_sig, all_classes: list, file_content: str | None = None
    ) -> PatternInstance | None:
        """
        Detect pattern based on configured depth.

        Args:
            class_sig: Class signature to analyze
            all_classes: All classes in the file for context
            file_content: Full file content (needed for 'full' depth)

        Returns:
            PatternInstance if pattern detected, None otherwise
        """
        if self.depth == "surface":
            return self.detect_surface(class_sig, all_classes)
        elif self.depth == "deep":
            # Try deep first, fallback to surface
            result = self.detect_deep(class_sig, all_classes)
            if result:
                return result
            return self.detect_surface(class_sig, all_classes)
        elif self.depth == "full":
            # Try full, fallback to deep, then surface
            if file_content:
                result = self.detect_full(class_sig, all_classes, file_content)
                if result:
                    return result
            result = self.detect_deep(class_sig, all_classes)
            if result:
                return result
            return self.detect_surface(class_sig, all_classes)
        else:
            raise ValueError(f"Invalid depth: {self.depth}")


class PatternRecognizer:
    """
    Main pattern recognition orchestrator.

    Coordinates multiple pattern detectors to analyze code.
    """

    def __init__(self, depth: str = "deep", enhance_with_ai: bool = True):
        """
        Initialize pattern recognizer.

        Args:
            depth: Detection depth ('surface', 'deep', 'full')
            enhance_with_ai: Enable AI enhancement of detected patterns (default: True, C3.6)
        """
        self.depth = depth
        self.enhance_with_ai = enhance_with_ai
        self.detectors: list[BasePatternDetector] = []
        self._register_detectors()

        # Initialize AI enhancer if enabled (C3.6)
        self.ai_enhancer = None
        if self.enhance_with_ai:
            try:
                from skill_seekers.cli.ai_enhancer import PatternEnhancer

                self.ai_enhancer = PatternEnhancer()
            except Exception as e:
                logger.warning(f"⚠️  Failed to initialize AI enhancer: {e}")
                self.enhance_with_ai = False

    def _register_detectors(self):
        """Register all available pattern detectors"""
        # Creational patterns (3)
        self.detectors.append(SingletonDetector(self.depth))
        self.detectors.append(FactoryDetector(self.depth))
        self.detectors.append(BuilderDetector(self.depth))

        # Structural patterns (2)
        self.detectors.append(DecoratorDetector(self.depth))
        self.detectors.append(AdapterDetector(self.depth))

        # Behavioral patterns (5)
        self.detectors.append(ObserverDetector(self.depth))
        self.detectors.append(StrategyDetector(self.depth))
        self.detectors.append(CommandDetector(self.depth))
        self.detectors.append(TemplateMethodDetector(self.depth))
        self.detectors.append(ChainOfResponsibilityDetector(self.depth))

    def analyze_file(self, file_path: str, content: str, language: str) -> PatternReport:
        """
        Analyze a single file for design patterns.

        Args:
            file_path: Path to source file
            content: File content
            language: Programming language

        Returns:
            PatternReport with detected patterns
        """
        # Step 1: Analyze code structure using CodeAnalyzer
        from skill_seekers.cli.code_analyzer import CodeAnalyzer

        analyzer = CodeAnalyzer(depth="deep")
        analysis = analyzer.analyze_file(file_path, content, language)

        if not analysis:
            return PatternReport(
                file_path=file_path,
                language=language,
                patterns=[],
                total_classes=0,
                total_functions=0,
                analysis_depth=self.depth,
            )

        classes = analysis.get("classes", [])
        functions = analysis.get("functions", [])

        # Convert to class signature objects
        class_sigs = self._convert_to_signatures(classes)

        # Step 2: Run pattern detection
        detected_patterns = []

        for class_sig in class_sigs:
            for detector in self.detectors:
                pattern = detector.detect(
                    class_sig=class_sig,
                    all_classes=class_sigs,
                    file_content=content if self.depth == "full" else None,
                )

                if pattern:
                    # Add file path to pattern
                    pattern.location = file_path

                    # Apply language-specific adaptations
                    pattern = LanguageAdapter.adapt_for_language(pattern, language)

                    detected_patterns.append(pattern)

        # Step 3: Enhance patterns with AI analysis (C3.6)
        if self.enhance_with_ai and self.ai_enhancer and detected_patterns:
            # Convert patterns to dict format for AI processing
            pattern_dicts = [p.to_dict() for p in detected_patterns]
            enhanced_dicts = self.ai_enhancer.enhance_patterns(pattern_dicts)

            # Update patterns with AI analysis
            for i, pattern in enumerate(detected_patterns):
                if i < len(enhanced_dicts) and "ai_analysis" in enhanced_dicts[i]:
                    pattern.ai_analysis = enhanced_dicts[i]["ai_analysis"]
                    # Apply confidence boost if provided
                    if "confidence" in enhanced_dicts[i]:
                        pattern.confidence = enhanced_dicts[i]["confidence"]

        return PatternReport(
            file_path=file_path,
            language=language,
            patterns=detected_patterns,
            total_classes=len(classes),
            total_functions=len(functions),
            analysis_depth=self.depth,
        )

    def _convert_to_signatures(self, classes: list[dict]):
        """
        Convert dict-based class analysis to signature objects.

        Note: Returns simple namespace objects that mimic ClassSignature structure
        but work with dict-based input from CodeAnalyzer.
        """
        from types import SimpleNamespace

        signatures = []

        for cls in classes:
            # Convert methods
            methods = []
            for method in cls.get("methods", []):
                # Convert parameters
                params = []
                for param in method.get("parameters", []):
                    param_obj = SimpleNamespace(
                        name=param.get("name", ""),
                        type_hint=param.get("type_hint"),
                        default=param.get("default"),
                    )
                    params.append(param_obj)

                method_obj = SimpleNamespace(
                    name=method.get("name", ""),
                    parameters=params,
                    return_type=method.get("return_type"),
                    docstring=method.get("docstring"),
                    line_number=method.get("line_number"),
                    is_async=method.get("is_async", False),
                    is_method=True,
                    decorators=method.get("decorators", []),
                )
                methods.append(method_obj)

            class_obj = SimpleNamespace(
                name=cls.get("name", ""),
                base_classes=cls.get("base_classes", []),
                methods=methods,
                docstring=cls.get("docstring"),
                line_number=cls.get("line_number"),
            )
            signatures.append(class_obj)

        return signatures


class SingletonDetector(BasePatternDetector):
    """
    Detect Singleton pattern.

    Singleton ensures a class has only one instance and provides global access.

    Detection Heuristics:
    - Surface: Class name contains 'Singleton'
    - Deep: Private constructor + static instance method
    - Full: Instance caching + thread safety checks

    Examples:
    - Python: __new__ override with instance caching
    - JavaScript: Module pattern or class with getInstance()
    - Java: Private constructor + synchronized getInstance()
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Singleton"
        self.category = "Creational"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check if class name suggests Singleton"""
        if "singleton" in class_sig.name.lower():
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.6,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=['Class name contains "Singleton"'],
            )
        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Check structural characteristics of Singleton"""
        evidence = []
        confidence = 0.0

        # Check for instance method (getInstance, instance, get_instance, etc.)
        instance_methods = [
            "getInstance",
            "instance",
            "get_instance",
            "Instance",
            "GetInstance",
            "INSTANCE",
        ]

        has_instance_method = False
        for method in class_sig.methods:
            if method.name in instance_methods:
                evidence.append(f"Has instance method: {method.name}")
                confidence += 0.4
                has_instance_method = True
                break

        # Check for private/protected constructor-like methods
        has_init_control = False
        for method in class_sig.methods:
            # Python: __init__ or __new__
            # Java/C#: private constructor (detected by naming)
            # Check if it has logic (not just pass)
            if method.name in ["__new__", "__init__", "constructor"] and (
                method.docstring or len(method.parameters) > 1
            ):
                evidence.append(f"Controlled initialization: {method.name}")
                confidence += 0.3
                has_init_control = True
                break

        # Check for class-level instance storage
        # This would require checking class attributes (future enhancement)

        if has_instance_method or has_init_control and confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface detection
        return self.detect_surface(class_sig, all_classes)

    def detect_full(
        self, class_sig, all_classes: list, file_content: str
    ) -> PatternInstance | None:
        """
        Full behavioral analysis for Singleton.

        Checks:
        - Instance caching in method body
        - Thread safety (locks, synchronized)
        - Lazy vs eager initialization
        """
        # Start with deep detection
        result = self.detect_deep(class_sig, all_classes)
        if not result:
            return None

        evidence = result.evidence.copy()
        confidence = result.confidence

        # Check for instance caching patterns in code
        caching_patterns = [
            "_instance",
            "__instance",
            "instance",
            "if not",
            "if self._instance is None",
            "synchronized",
            "Lock()",
            "threading",
        ]

        for pattern in caching_patterns:
            if pattern in file_content and pattern not in " ".join(evidence):
                evidence.append(f"Instance caching detected: {pattern}")
                confidence += 0.1

        # Cap confidence at 0.95 (never 100% certain without runtime analysis)
        result.confidence = min(confidence, 0.95)
        result.evidence = evidence

        return result


class FactoryDetector(BasePatternDetector):
    """
    Detect Factory pattern (Factory Method and Abstract Factory).

    Factory defines an interface for creating objects, letting subclasses decide
    which class to instantiate.

    Detection Heuristics:
    - Surface: Class/method name contains 'Factory', 'create', 'make'
    - Deep: Method returns different object types based on parameters
    - Full: Polymorphic object creation with inheritance hierarchy

    Examples:
    - createProduct(type) -> Product
    - ProductFactory with createProductA(), createProductB()
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Factory"
        self.category = "Creational"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming conventions for Factory"""
        # Check class name
        if "factory" in class_sig.name.lower():
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.7,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=['Class name contains "Factory"'],
            )

        # Check for factory methods
        factory_method_names = ["create", "make", "build", "new", "get"]
        for method in class_sig.methods:
            method_lower = method.name.lower()
            # Check if method returns something (has return type or is not void)
            if any(name in method_lower for name in factory_method_names) and (
                method.return_type or "create" in method_lower
            ):
                return PatternInstance(
                    pattern_type=self.pattern_type,
                    category=self.category,
                    confidence=0.6,
                    location="",
                    class_name=class_sig.name,
                    method_name=method.name,
                    line_number=method.line_number,
                    evidence=[f"Factory method detected: {method.name}"],
                )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Factory"""
        evidence = []
        confidence = 0.0
        factory_methods = []

        # Look for methods that create objects
        creation_keywords = ["create", "make", "build", "new", "construct", "get"]

        for method in class_sig.methods:
            method_lower = method.name.lower()

            # Check if method name suggests object creation
            if any(keyword in method_lower for keyword in creation_keywords):
                factory_methods.append(method.name)
                confidence += 0.3

                # Check if it takes parameters (suggests different object types)
                if len(method.parameters) > 1:  # >1 because 'self' counts
                    evidence.append(f"Parameterized factory method: {method.name}")
                    confidence += 0.2
                else:
                    evidence.append(f"Factory method: {method.name}")

        # Check if multiple factory methods exist (Abstract Factory pattern)
        if len(factory_methods) >= 2:
            evidence.append(f"Multiple factory methods: {', '.join(factory_methods[:3])}")
            confidence += 0.2

        # Check for inheritance (factory hierarchy)
        if class_sig.base_classes:
            evidence.append(f"Inherits from: {', '.join(class_sig.base_classes)}")
            confidence += 0.1

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
                related_classes=class_sig.base_classes,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class ObserverDetector(BasePatternDetector):
    """
    Detect Observer pattern (Pub/Sub).

    Observer defines one-to-many dependency where multiple objects
    observe and react to state changes.

    Detection Heuristics:
    - Surface: Class/method names with 'Observer', 'Listener', 'Subscribe'
    - Deep: attach/detach + notify methods
    - Full: Collection of observers + iteration pattern

    Examples:
    - addObserver(), removeObserver(), notifyObservers()
    - addEventListener(), removeEventListener(), emit()
    - subscribe(), unsubscribe(), publish()
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Observer"
        self.category = "Behavioral"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Observer pattern"""
        observer_keywords = ["observer", "listener", "subscriber", "watcher"]

        # Check class name
        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in observer_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.6,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests Observer: {class_sig.name}"],
            )

        # Check method names
        observer_methods = [
            "subscribe",
            "unsubscribe",
            "publish",
            "addobserver",
            "removeobserver",
            "notify",
            "addeventlistener",
            "removeeventlistener",
            "emit",
            "attach",
            "detach",
            "update",
        ]

        for method in class_sig.methods:
            method_lower = method.name.lower().replace("_", "")
            if any(obs_method in method_lower for obs_method in observer_methods):
                return PatternInstance(
                    pattern_type=self.pattern_type,
                    category=self.category,
                    confidence=0.65,
                    location="",
                    class_name=class_sig.name,
                    method_name=method.name,
                    line_number=method.line_number,
                    evidence=[f"Observer method detected: {method.name}"],
                )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Observer"""
        evidence = []
        confidence = 0.0

        # Look for characteristic method triplet: attach/detach/notify
        has_attach = False
        has_detach = False
        has_notify = False

        attach_names = ["attach", "add", "subscribe", "register", "addeventlistener"]
        detach_names = [
            "detach",
            "remove",
            "unsubscribe",
            "unregister",
            "removeeventlistener",
        ]
        notify_names = ["notify", "update", "emit", "publish", "fire", "trigger"]

        for method in class_sig.methods:
            method_lower = method.name.lower().replace("_", "")

            if any(name in method_lower for name in attach_names):
                has_attach = True
                evidence.append(f"Attach method: {method.name}")
                confidence += 0.3

            if any(name in method_lower for name in detach_names):
                has_detach = True
                evidence.append(f"Detach method: {method.name}")
                confidence += 0.3

            if any(name in method_lower for name in notify_names):
                has_notify = True
                evidence.append(f"Notify method: {method.name}")
                confidence += 0.3

        # Strong signal if has all three
        if has_attach and has_detach and has_notify:
            confidence = min(confidence, 0.95)

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.95),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class StrategyDetector(BasePatternDetector):
    """
    Detect Strategy pattern.

    Strategy defines a family of algorithms, encapsulates each one,
    and makes them interchangeable.

    Detection Heuristics:
    - Surface: Class/method names with 'Strategy', 'Policy', 'Algorithm'
    - Deep: Interface with single key method + multiple implementations
    - Full: Composition with interchangeable strategy objects

    Examples:
    - SortStrategy with sort() method
    - PaymentStrategy with pay() method
    - CompressionStrategy with compress() method
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Strategy"
        self.category = "Behavioral"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Strategy"""
        strategy_keywords = ["strategy", "policy", "algorithm"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in strategy_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.7,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests Strategy: {class_sig.name}"],
            )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Strategy"""
        evidence = []
        confidence = 0.0

        # Strategy pattern often involves:
        # 1. Base class/interface with key method
        # 2. Multiple subclasses implementing same interface

        # Check if this class is a concrete strategy
        if class_sig.base_classes:
            base_class = class_sig.base_classes[0] if class_sig.base_classes else None

            # Look for siblings (other strategies with same base)
            siblings = [
                cls.name
                for cls in all_classes
                if cls.base_classes
                and base_class in cls.base_classes
                and cls.name != class_sig.name
            ]

            if siblings:
                evidence.append(f"Part of strategy family with: {', '.join(siblings[:3])}")
                confidence += 0.5

            if base_class and ("strategy" in base_class.lower() or "policy" in base_class.lower()):
                evidence.append(f"Inherits from strategy base: {base_class}")
                confidence += 0.3

        # Check if this is a strategy base class
        # (has subclasses in same file)
        subclasses = [cls.name for cls in all_classes if class_sig.name in cls.base_classes]

        if len(subclasses) >= 2:
            evidence.append(f"Strategy base with implementations: {', '.join(subclasses[:3])}")
            confidence += 0.6

        # Check for single dominant method (strategy interface)
        if len(class_sig.methods) == 1 or len(class_sig.methods) == 2:
            # Single method or method + __init__
            main_method = [m for m in class_sig.methods if m.name not in ["__init__", "__new__"]]
            if main_method:
                evidence.append(f"Strategy interface method: {main_method[0].name}")
                confidence += 0.2

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
                related_classes=class_sig.base_classes + subclasses,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class DecoratorDetector(BasePatternDetector):
    """
    Detect Decorator pattern.

    Decorator attaches additional responsibilities to an object dynamically,
    providing flexible alternative to subclassing.

    Detection Heuristics:
    - Surface: Class name contains 'Decorator', 'Wrapper'
    - Deep: Wraps same interface, delegates to wrapped object
    - Full: Composition + delegation + interface matching

    Examples:
    - LoggingDecorator wraps Service
    - CachingDecorator wraps DataFetcher
    - Python @decorator syntax
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Decorator"
        self.category = "Structural"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Decorator"""
        decorator_keywords = ["decorator", "wrapper", "proxy"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in decorator_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.65,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests Decorator: {class_sig.name}"],
            )

        # Check for Python decorator syntax
        for method in class_sig.methods:
            if method.decorators:
                # Has decorators - might be using decorator pattern
                # But this is too common, so low confidence
                return PatternInstance(
                    pattern_type=self.pattern_type,
                    category=self.category,
                    confidence=0.3,
                    location="",
                    class_name=class_sig.name,
                    method_name=method.name,
                    line_number=method.line_number,
                    evidence=[f"Method uses decorators: {method.decorators}"],
                )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Decorator"""
        evidence = []
        confidence = 0.0

        # Decorator pattern characteristics:
        # 1. Has same base class as wrapped object
        # 2. Takes wrapped object in constructor
        # 3. Delegates calls to wrapped object

        # Check if shares base class with other classes
        if class_sig.base_classes:
            base_class = class_sig.base_classes[0]

            # Find other classes with same base
            siblings = [
                cls.name
                for cls in all_classes
                if cls.base_classes
                and base_class in cls.base_classes
                and cls.name != class_sig.name
            ]

            if siblings:
                evidence.append(f"Shares interface with: {', '.join(siblings[:2])}")
                confidence += 0.3

        # Check __init__ for composition (takes object parameter)
        init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
        # Check if takes object parameter (not just self)
        if init_method and len(init_method.parameters) > 1:  # More than just 'self'
            param_names = [p.name for p in init_method.parameters if p.name != "self"]
            if any(
                name in ["wrapped", "component", "inner", "obj", "target"] for name in param_names
            ):
                evidence.append(f"Takes wrapped object in constructor: {param_names}")
                confidence += 0.4

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.85),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
                related_classes=class_sig.base_classes,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class BuilderDetector(BasePatternDetector):
    """
    Detect Builder pattern.

    Builder separates construction of complex object from its representation,
    allowing same construction process to create different representations.

    Detection Heuristics:
    - Surface: Class name contains 'Builder'
    - Deep: Fluent interface (methods return self), build()/create() terminal method
    - Full: Multiple configuration methods + final build step

    Examples:
    - QueryBuilder with where(), orderBy(), build()
    - RequestBuilder with setHeader(), setBody(), execute()
    - StringBuilder pattern
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Builder"
        self.category = "Creational"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Builder"""
        if "builder" in class_sig.name.lower():
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.7,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f'Class name contains "Builder": {class_sig.name}'],
            )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Builder"""
        evidence = []
        confidence = 0.0

        # Builder characteristics:
        # 1. Multiple setter/configuration methods
        # 2. Terminal build()/create()/execute() method
        # 3. Fluent interface (methods return self/this)

        # Check for build/create terminal method
        terminal_methods = ["build", "create", "execute", "construct", "make"]
        has_terminal = any(
            m.name.lower() in terminal_methods or m.name.lower().startswith("build")
            for m in class_sig.methods
        )

        if has_terminal:
            evidence.append("Has terminal build/create method")
            confidence += 0.4

        # Check for setter methods (with_, set_, add_)
        setter_prefixes = ["with", "set", "add", "configure"]
        setter_count = sum(
            1
            for m in class_sig.methods
            if any(m.name.lower().startswith(prefix) for prefix in setter_prefixes)
        )

        if setter_count >= 3:
            evidence.append(f"Has {setter_count} configuration methods")
            confidence += 0.4
        elif setter_count >= 1:
            confidence += 0.2

        # Check method count (builders typically have many methods)
        if len(class_sig.methods) >= 5:
            confidence += 0.1

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)

    def detect_full(
        self, class_sig, all_classes: list, file_content: str
    ) -> PatternInstance | None:
        """Full behavioral analysis for Builder"""
        # Start with deep detection
        pattern = self.detect_deep(class_sig, all_classes)
        if not pattern:
            return None

        evidence = list(pattern.evidence)
        confidence = pattern.confidence

        # Look for fluent interface pattern (return self/this)
        class_content = file_content.lower()
        fluent_indicators = ["return self", "return this"]

        if any(indicator in class_content for indicator in fluent_indicators):
            evidence.append("Uses fluent interface (return self)")
            confidence += 0.1

        # Check for complex object construction (multiple fields)
        if "self." in class_content and class_content.count("self.") >= 5:
            evidence.append("Builds complex object with multiple fields")
            confidence += 0.05

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.95),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to deep
        return self.detect_deep(class_sig, all_classes)


class AdapterDetector(BasePatternDetector):
    """
    Detect Adapter pattern.

    Adapter converts interface of a class into another interface clients expect,
    allowing incompatible interfaces to work together.

    Detection Heuristics:
    - Surface: Class name contains 'Adapter', 'Wrapper'
    - Deep: Wraps external/incompatible class, translates method calls
    - Full: Composition + delegation with interface translation

    Examples:
    - DatabaseAdapter wraps external DB library
    - ApiAdapter translates REST to internal interface
    - FileSystemAdapter wraps OS file operations
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Adapter"
        self.category = "Structural"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Adapter"""
        adapter_keywords = ["adapter", "wrapper", "bridge"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in adapter_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.7,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests Adapter: {class_sig.name}"],
            )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Adapter"""
        evidence = []
        confidence = 0.0

        # Adapter characteristics:
        # 1. Takes adaptee in constructor
        # 2. Implements target interface
        # 3. Delegates to adaptee with translation

        # Check __init__ for composition (takes adaptee)
        init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
        if init_method and len(init_method.parameters) > 1:
            param_names = [p.name for p in init_method.parameters if p.name != "self"]
            adaptee_names = ["adaptee", "wrapped", "client", "service", "api", "source"]
            if any(name in param_names for name in adaptee_names):
                evidence.append(f"Takes adaptee in constructor: {param_names}")
                confidence += 0.4

        # Check if implements interface (has base class)
        if class_sig.base_classes:
            evidence.append(f"Implements interface: {class_sig.base_classes[0]}")
            confidence += 0.3

        # Check for delegation methods (methods that likely call adaptee)
        if len(class_sig.methods) >= 3:  # Multiple interface methods
            evidence.append(f"Has {len(class_sig.methods)} interface methods")
            confidence += 0.2

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.85),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class CommandDetector(BasePatternDetector):
    """
    Detect Command pattern.

    Command encapsulates a request as an object, allowing parameterization
    of clients with different requests, queuing, logging, and undo operations.

    Detection Heuristics:
    - Surface: Class name contains 'Command', 'Action', 'Task'
    - Deep: Has execute()/run() method, encapsulates action
    - Full: Receiver composition + undo support

    Examples:
    - SaveCommand with execute() method
    - UndoableCommand with undo() and redo()
    - TaskCommand in task queue
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "Command"
        self.category = "Behavioral"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Command"""
        command_keywords = ["command", "action", "task", "operation"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in command_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.65,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests Command: {class_sig.name}"],
            )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Command"""
        evidence = []
        confidence = 0.0

        # Command characteristics:
        # 1. Has execute()/run()/call() method
        # 2. May have undo()/redo() methods
        # 3. Encapsulates receiver and parameters

        # Check for execute/run method
        execute_methods = ["execute", "run", "call", "do", "perform", "__call__"]
        has_execute = any(m.name.lower() in execute_methods for m in class_sig.methods)

        if has_execute:
            method_name = next(
                m.name for m in class_sig.methods if m.name.lower() in execute_methods
            )
            evidence.append(f"Has execute method: {method_name}()")
            confidence += 0.5

        # Check for undo/redo support
        undo_methods = ["undo", "rollback", "revert", "redo"]
        has_undo = any(m.name.lower() in undo_methods for m in class_sig.methods)

        if has_undo:
            evidence.append("Supports undo/redo operations")
            confidence += 0.3

        # Check for receiver (takes object in __init__)
        init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
        if init_method and len(init_method.parameters) > 1:
            evidence.append("Encapsulates receiver/parameters")
            confidence += 0.2

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class TemplateMethodDetector(BasePatternDetector):
    """
    Detect Template Method pattern.

    Template Method defines skeleton of algorithm in base class,
    letting subclasses override specific steps without changing structure.

    Detection Heuristics:
    - Surface: Abstract/Base class with template-like names
    - Deep: Abstract base with hook methods, concrete subclasses override
    - Full: Template method calls abstract/hook methods

    Examples:
    - AbstractProcessor with process() calling abstract steps
    - BaseParser with parse() template method
    - Framework base classes with lifecycle hooks
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "TemplateMethod"
        self.category = "Behavioral"

    def detect_surface(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Check naming for Template Method"""
        template_keywords = ["abstract", "base", "template"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in template_keywords):
            # Check if has subclasses
            subclasses = [cls.name for cls in all_classes if class_sig.name in cls.base_classes]

            if subclasses:
                return PatternInstance(
                    pattern_type=self.pattern_type,
                    category=self.category,
                    confidence=0.6,
                    location="",
                    class_name=class_sig.name,
                    line_number=class_sig.line_number,
                    evidence=[f"Abstract base with subclasses: {', '.join(subclasses[:2])}"],
                    related_classes=subclasses,
                )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Template Method"""
        evidence = []
        confidence = 0.0

        # Template Method characteristics:
        # 1. Has subclasses (is base class)
        # 2. Has methods that look like hooks (prepare, validate, cleanup, etc.)
        # 3. Has template method that orchestrates

        # Check for subclasses
        subclasses = [cls.name for cls in all_classes if class_sig.name in cls.base_classes]

        if len(subclasses) >= 1:
            evidence.append(f"Base class with {len(subclasses)} implementations")
            confidence += 0.4

        # Check for hook-like method names
        hook_keywords = [
            "prepare",
            "initialize",
            "validate",
            "process",
            "finalize",
            "setup",
            "teardown",
            "before",
            "after",
            "pre",
            "post",
            "hook",
        ]

        hook_methods = [
            m.name
            for m in class_sig.methods
            if any(keyword in m.name.lower() for keyword in hook_keywords)
        ]

        if len(hook_methods) >= 2:
            evidence.append(f"Has hook methods: {', '.join(hook_methods[:3])}")
            confidence += 0.3

        # Check for abstract methods (no implementation or pass/raise)
        abstract_methods = [
            m.name
            for m in class_sig.methods
            if m.name.startswith("_") or "abstract" in m.name.lower()
        ]

        if abstract_methods:
            evidence.append(f"Has abstract methods: {', '.join(abstract_methods[:2])}")
            confidence += 0.2

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.85),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
                related_classes=subclasses,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class ChainOfResponsibilityDetector(BasePatternDetector):
    """
    Detect Chain of Responsibility pattern.

    Chain of Responsibility passes request along chain of handlers until
    one handles it, avoiding coupling sender to receiver.

    Detection Heuristics:
    - Surface: Class name contains 'Handler', 'Chain', 'Middleware'
    - Deep: Has next/successor reference, handle() method
    - Full: Chain traversal logic, request passing

    Examples:
    - LogHandler with next handler
    - AuthMiddleware chain
    - EventHandler chain
    """

    def __init__(self, depth: str = "deep"):
        super().__init__(depth)
        self.pattern_type = "ChainOfResponsibility"
        self.category = "Behavioral"

    def detect_surface(self, class_sig, _all_classes: list) -> PatternInstance | None:
        """Check naming for Chain of Responsibility"""
        chain_keywords = ["handler", "chain", "middleware", "filter", "processor"]

        class_lower = class_sig.name.lower()
        if any(keyword in class_lower for keyword in chain_keywords):
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=0.6,
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=[f"Class name suggests handler chain: {class_sig.name}"],
            )

        return None

    def detect_deep(self, class_sig, all_classes: list) -> PatternInstance | None:
        """Structural analysis for Chain of Responsibility"""
        evidence = []
        confidence = 0.0

        # Chain of Responsibility characteristics:
        # 1. Has handle()/process() method
        # 2. Has next/successor reference
        # 3. May have set_next() method

        # Check for handle/process method
        handle_methods = ["handle", "process", "execute", "filter", "middleware"]
        has_handle = any(
            m.name.lower() in handle_methods or m.name.lower().startswith("handle")
            for m in class_sig.methods
        )

        if has_handle:
            evidence.append("Has handle/process method")
            confidence += 0.4

        # Check for next/successor methods or parameters
        init_method = next((m for m in class_sig.methods if m.name == "__init__"), None)
        has_next_ref = False

        if init_method:
            param_names = [p.name for p in init_method.parameters if p.name != "self"]
            next_names = ["next", "successor", "next_handler", "next_middleware"]

            if any(name in param_names for name in next_names):
                evidence.append("Takes next handler in chain")
                confidence += 0.3
                has_next_ref = True

        # Check for set_next() method
        has_set_next = any(
            "next" in m.name.lower() and ("set" in m.name.lower() or "add" in m.name.lower())
            for m in class_sig.methods
        )

        if has_set_next:
            evidence.append("Has set_next() method")
            confidence += 0.3
            has_next_ref = True

        # Check if part of handler family (shares base class)
        if class_sig.base_classes:
            base_class = class_sig.base_classes[0]
            siblings = [
                cls.name
                for cls in all_classes
                if cls.base_classes
                and base_class in cls.base_classes
                and cls.name != class_sig.name
            ]

            if siblings and has_next_ref:
                evidence.append(f"Part of handler chain with: {', '.join(siblings[:2])}")
                confidence += 0.2

        if confidence >= 0.5:
            return PatternInstance(
                pattern_type=self.pattern_type,
                category=self.category,
                confidence=min(confidence, 0.9),
                location="",
                class_name=class_sig.name,
                line_number=class_sig.line_number,
                evidence=evidence,
            )

        # Fallback to surface
        return self.detect_surface(class_sig, all_classes)


class LanguageAdapter:
    """
    Language-specific pattern detection adaptations.

    Adjusts pattern confidence based on language idioms and conventions.
    Different languages have different ways of implementing patterns.
    """

    @staticmethod
    def adapt_for_language(pattern: PatternInstance, language: str) -> PatternInstance:
        """
        Adjust confidence based on language-specific idioms.

        Args:
            pattern: Detected pattern instance
            language: Programming language

        Returns:
            Adjusted pattern instance with language-specific confidence
        """
        if not pattern:
            return pattern

        evidence_str = " ".join(pattern.evidence).lower()

        # Python-specific adaptations
        if language == "Python":
            # Decorator pattern: Python has native @ syntax
            if pattern.pattern_type == "Decorator":
                if "@" in " ".join(pattern.evidence):
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Python @decorator syntax detected")

            # Singleton: __new__ method is Python idiom
            elif pattern.pattern_type == "Singleton":
                if "__new__" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)

            # Strategy: Duck typing common in Python
            elif (
                pattern.pattern_type == "Strategy"
                and "duck typing" in evidence_str
                or "protocol" in evidence_str
            ):
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

        # JavaScript/TypeScript adaptations
        elif language in ["JavaScript", "TypeScript"]:
            # Singleton: Module pattern is common
            if pattern.pattern_type == "Singleton":
                if "module" in evidence_str or "export default" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("JavaScript module pattern")

            # Factory: Factory functions are idiomatic
            elif pattern.pattern_type == "Factory":
                if "create" in evidence_str or "make" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.05, 1.0)

            # Observer: Event emitters are built-in
            elif (
                pattern.pattern_type == "Observer"
                and "eventemitter" in evidence_str
                or "event" in evidence_str
            ):
                pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                pattern.evidence.append("EventEmitter pattern detected")

        # Java/C# adaptations (interface-heavy languages)
        elif language in ["Java", "C#"]:
            # All patterns: Interfaces are explicit
            if "interface" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

            # Factory: Abstract Factory common
            if pattern.pattern_type == "Factory":
                if "abstract" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Abstract Factory pattern")

            # Template Method: Abstract classes common
            elif pattern.pattern_type == "TemplateMethod" and "abstract" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.1, 1.0)

        # Kotlin adaptations
        elif language == "Kotlin":
            # Singleton: object declaration is the idiomatic Kotlin singleton
            if pattern.pattern_type == "Singleton":
                if "object" in evidence_str or "companion" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.15, 1.0)
                    pattern.evidence.append("Kotlin object declaration (singleton)")

            # Factory: companion object with create/of methods
            elif pattern.pattern_type == "Factory":
                if "companion" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Kotlin companion object factory")

            # Strategy: sealed class/interface with when expression
            elif pattern.pattern_type == "Strategy":
                if "sealed" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.15, 1.0)
                    pattern.evidence.append("Kotlin sealed class/interface strategy")

            # Builder: data class copy() or DSL builder pattern
            elif pattern.pattern_type == "Builder":
                if "data" in evidence_str or "apply" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Kotlin data class / DSL builder")

            # Observer: Flow/StateFlow is the coroutine-based observer
            elif pattern.pattern_type == "Observer":
                if "flow" in evidence_str or "stateflow" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Kotlin Flow/StateFlow observer")

            # Decorator: extension functions serve as lightweight decorators
            elif pattern.pattern_type == "Decorator" and "extension" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)
                pattern.evidence.append("Kotlin extension function decorator")

        # Go adaptations
        elif language == "Go":
            # Singleton: sync.Once is idiomatic
            if pattern.pattern_type == "Singleton":
                if "sync.once" in evidence_str or "once.do" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.15, 1.0)
                    pattern.evidence.append("Go sync.Once idiom")

            # Strategy: Interfaces are implicit
            elif pattern.pattern_type == "Strategy" and "interface{}" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

        # Rust adaptations
        elif language == "Rust":
            # Singleton: Lazy static is common
            if pattern.pattern_type == "Singleton":
                if "lazy_static" in evidence_str or "oncecell" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.15, 1.0)
                    pattern.evidence.append("Rust lazy_static/OnceCell")

            # Builder: Derive builder is idiomatic
            elif pattern.pattern_type == "Builder":
                if "derive" in evidence_str and "builder" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)

            # Adapter: Trait adapters are common
            elif pattern.pattern_type == "Adapter" and "trait" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.1, 1.0)

        # C++ adaptations
        elif language == "C++":
            # Singleton: Meyer's Singleton is idiomatic
            if pattern.pattern_type == "Singleton":
                if "static" in evidence_str and "local" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)
                    pattern.evidence.append("Meyer's Singleton (static local)")

            # Factory: Template-based factories
            elif pattern.pattern_type == "Factory" and "template" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

        # Ruby adaptations
        elif language == "Ruby":
            # Singleton: Ruby has Singleton module
            if pattern.pattern_type == "Singleton":
                if "include singleton" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.2, 1.0)
                    pattern.evidence.append("Ruby Singleton module")

            # Builder: Method chaining is idiomatic
            elif pattern.pattern_type == "Builder" and "method chaining" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

        # PHP adaptations
        elif language == "PHP":
            # Singleton: Private constructor is common
            if pattern.pattern_type == "Singleton":
                if "private" in evidence_str and "__construct" in evidence_str:
                    pattern.confidence = min(pattern.confidence + 0.1, 1.0)

            # Factory: Static factory methods
            elif pattern.pattern_type == "Factory" and "static" in evidence_str:
                pattern.confidence = min(pattern.confidence + 0.05, 1.0)

        return pattern


# ============================================================================
# PATTERN FILTERING UTILITIES (Issue #240 - C4.2)
# ============================================================================


def filter_patterns_by_confidence(patterns: list[dict], min_confidence: float) -> list[dict]:
    """
    Filter patterns by minimum confidence threshold.

    Args:
        patterns: List of pattern dictionaries (from PatternReport.to_dict())
        min_confidence: Minimum confidence threshold (0.0-1.0)

    Returns:
        Filtered list of patterns meeting the threshold
    """
    filtered = []
    for pattern in patterns:
        if pattern.get("confidence", 0.0) >= min_confidence:
            filtered.append(pattern)
    return filtered


def create_multi_level_report(pattern_results: list[dict]) -> dict:
    """
    Create multi-level pattern report with different confidence thresholds.

    Args:
        pattern_results: List of PatternReport dictionaries

    Returns:
        Dictionary with patterns grouped by confidence level:
        - all_patterns: All detected patterns
        - high_confidence: Patterns >= 0.70 (for detailed analysis)
        - critical: Patterns >= 0.80 (for ARCHITECTURE.md)
        - statistics: Pattern count by level
    """
    # Flatten all patterns from all files
    all_patterns = []
    for report in pattern_results:
        file_path = report.get("file_path", "unknown")
        for pattern in report.get("patterns", []):
            # Add file path to pattern for context
            pattern_with_file = {**pattern, "file_path": file_path}
            all_patterns.append(pattern_with_file)

    # Sort by confidence (highest first)
    all_patterns_sorted = sorted(all_patterns, key=lambda p: p.get("confidence", 0.0), reverse=True)

    # Filter by confidence levels
    critical = filter_patterns_by_confidence(all_patterns_sorted, CONFIDENCE_THRESHOLDS["critical"])
    high_confidence = filter_patterns_by_confidence(
        all_patterns_sorted, CONFIDENCE_THRESHOLDS["high"]
    )
    medium = filter_patterns_by_confidence(all_patterns_sorted, CONFIDENCE_THRESHOLDS["medium"])

    return {
        "all_patterns": all_patterns_sorted,
        "critical": critical,
        "high_confidence": high_confidence,
        "medium": medium,
        "statistics": {
            "total": len(all_patterns_sorted),
            "critical_count": len(critical),
            "high_confidence_count": len(high_confidence),
            "medium_count": len(medium),
            "low_count": len(all_patterns_sorted) - len(medium),
        },
        "thresholds": CONFIDENCE_THRESHOLDS,
    }


def main():
    """
    CLI entry point for pattern detection.

    Usage:
        skill-seekers-patterns --file src/database.py
        skill-seekers-patterns --directory src/ --output patterns/
        skill-seekers-patterns --file app.py --depth full --json
    """
    import sys

    parser = argparse.ArgumentParser(
        description="Detect design patterns in source code",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Analyze single file
  skill-seekers-patterns --file src/database.py

  # Analyze directory
  skill-seekers-patterns --directory src/ --output patterns/

  # Full analysis with JSON output
  skill-seekers-patterns --file app.py --depth full --json

  # Multiple files
  skill-seekers-patterns --file src/db.py --file src/api.py

Supported Languages:
  Python, JavaScript, TypeScript, C++, C, C#, Go, Rust, Java, Ruby, PHP
""",
    )

    parser.add_argument(
        "--file",
        action="append",
        help="Source file to analyze (can be specified multiple times)",
    )
    parser.add_argument("--directory", help="Directory to analyze (analyzes all source files)")
    parser.add_argument(
        "--output", help="Output directory for results (default: current directory)"
    )
    parser.add_argument(
        "--depth",
        choices=["surface", "deep", "full"],
        default="deep",
        help="Detection depth: surface (fast), deep (default), full (thorough)",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="Output JSON format instead of human-readable",
    )
    parser.add_argument("--verbose", action="store_true", help="Enable verbose output")

    args = parser.parse_args()

    # Validate inputs
    if not args.file and not args.directory:
        parser.error("Must specify either --file or --directory")

    # Create recognizer
    recognizer = PatternRecognizer(depth=args.depth)

    # Collect files to analyze
    files_to_analyze = []

    if args.file:
        for file_path in args.file:
            path = Path(file_path)
            if not path.exists():
                print(f"Error: File not found: {file_path}", file=sys.stderr)
                return 1
            files_to_analyze.append(path)

    if args.directory:
        from skill_seekers.cli.codebase_scraper import detect_language, walk_directory

        directory = Path(args.directory)
        if not directory.exists():
            print(f"Error: Directory not found: {args.directory}", file=sys.stderr)
            return 1

        # Walk directory for source files
        files_to_analyze.extend(walk_directory(directory))

    if not files_to_analyze:
        print("No source files found to analyze", file=sys.stderr)
        return 1

    # Analyze files
    all_reports = []
    total_patterns = 0

    for file_path in files_to_analyze:
        try:
            from skill_seekers.cli.codebase_scraper import detect_language

            content = file_path.read_text(encoding="utf-8", errors="ignore")
            language = detect_language(file_path)

            if language == "Unknown":
                if args.verbose:
                    print(f"Skipping {file_path}: Unknown language")
                continue

            report = recognizer.analyze_file(str(file_path), content, language)

            if report.patterns:
                all_reports.append(report)
                total_patterns += len(report.patterns)

                if not args.json and args.verbose:
                    print(f"\n{file_path}:")
                    for pattern in report.patterns:
                        print(
                            f"  [{pattern.pattern_type}] {pattern.class_name} (confidence: {pattern.confidence:.2f})"
                        )

        except Exception as e:
            if args.verbose:
                print(f"Error analyzing {file_path}: {e}", file=sys.stderr)
            continue

    # Output results
    if args.json:
        # JSON output
        output_data = {
            "total_files_analyzed": len(files_to_analyze),
            "files_with_patterns": len(all_reports),
            "total_patterns_detected": total_patterns,
            "reports": [report.to_dict() for report in all_reports],
        }

        if args.output:
            output_path = Path(args.output) / "detected_patterns.json"
            output_path.parent.mkdir(parents=True, exist_ok=True)
            with open(output_path, "w", encoding="utf-8") as f:
                json.dump(output_data, f, indent=2)
            print(f"Results saved to: {output_path}")
        else:
            print(json.dumps(output_data, indent=2))

    else:
        # Human-readable output
        print(f"\n{'=' * 60}")
        print("PATTERN DETECTION RESULTS")
        print(f"{'=' * 60}")
        print(f"Files analyzed: {len(files_to_analyze)}")
        print(f"Files with patterns: {len(all_reports)}")
        print(f"Total patterns detected: {total_patterns}")
        print(f"{'=' * 60}\n")

        # Pattern summary by type
        pattern_counts = {}
        for report in all_reports:
            for pattern in report.patterns:
                pattern_counts[pattern.pattern_type] = (
                    pattern_counts.get(pattern.pattern_type, 0) + 1
                )

        if pattern_counts:
            print("Pattern Summary:")
            for pattern_type, count in sorted(
                pattern_counts.items(), key=lambda x: x[1], reverse=True
            ):
                print(f"  {pattern_type}: {count}")
            print()

        # Detailed results
        if all_reports:
            print("Detected Patterns:\n")
            for report in all_reports:
                print(f"{report.file_path}:")
                for pattern in report.patterns:
                    print(f"  • {pattern.pattern_type} - {pattern.class_name}")
                    print(f"    Confidence: {pattern.confidence:.2f}")
                    print(f"    Category: {pattern.category}")
                    if pattern.evidence:
                        print(f"    Evidence: {pattern.evidence[0]}")
                    print()

    return 0


if __name__ == "__main__":
    sys.exit(main())
