"""
Legacy Pattern Detector for Hephaestus

Comprehensive v1.0+ pattern detection for Anvil projects.
Supports detection of legacy patterns, mixed architectures, and migration opportunities.
"""

import re
from pathlib import Path
from typing import Dict, List, Any, Optional
from dataclasses import dataclass

from ..patterns.pattern_library import PatternLibrary


@dataclass
class LegacyPattern:
    """Represents a detected legacy pattern."""

    name: str
    category: str
    description: str
    file_path: Path
    line_numbers: List[int]
    severity: str  # "low", "medium", "high", "critical"
    migration_complexity: str  # "simple", "moderate", "complex", "expert"
    modern_alternative: Optional[str] = None
    breaking_change_risk: str = "low"  # "low", "medium", "high"
    dependencies: Optional[List[str]] = None

    def __post_init__(self):
        if self.dependencies is None:
            self.dependencies = []


@dataclass
class PatternCompatibility:
    """Compatibility information for pattern combinations."""

    pattern1: str
    pattern2: str
    compatibility_score: float  # 0.0-1.0
    conflicts: List[str]
    synergies: List[str]
    migration_path: Optional[str] = None


class LegacyPatternDetector:
    """Comprehensive legacy pattern detection for Anvil v1.0+ projects."""

    def __init__(self, file_system, project_manager):
        self.file_system = file_system
        self.project_manager = project_manager
        self.pattern_library = PatternLibrary()

        # Legacy-specific patterns beyond basic pattern library
        self._legacy_patterns = {
            "anvil_extras_routing": {
                "indicators": [
                    r"from anvil_extras\.routing",
                    r"@anvil_extras\.routing\.route",
                    r"anvil_extras\.routing\.set_url_hash",
                    r"anvil_extras\.routing\.get_url_hash",
                ],
                "description": "Anvil Extras routing system (legacy)",
                "modern_alternative": "Official Anvil routing",
                "migration_complexity": "moderate",
                "breaking_change_risk": "medium",
            },
            "classic_components_only": {
                "indicators": [
                    r"ColumnPanel|FlowPanel|GridPanel|XYPanel",
                    r"Button\([^)]*\)",
                    r"TextBox\([^)]*\)",
                    r"Label\([^)]*\)",
                    r"DataGrid\([^)]*\)",
                ],
                "exclusions": [
                    r"NavigationDrawerLayout|NavigationRailLayout",
                    r"role:\s*['\"](?:outlined|filled|tonal)-['\"]",
                    r"@theme\.",
                ],
                "description": "Classic Anvil components without Material 3",
                "modern_alternative": "Material 3 components",
                "migration_complexity": "moderate",
                "breaking_change_risk": "low",
            },
            "raw_table_access": {
                "indicators": [
                    r"app_tables\.\w+\.get\(",
                    r"app_tables\.\w+\.search\(",
                    r"app_tables\.\w+\.add_row\(",
                    r"app_tables\.\w+\.update\(",
                    r"app_tables\.\w+\.delete\(",
                ],
                "description": "Direct data table access",
                "modern_alternative": "Model classes",
                "migration_complexity": "complex",
                "breaking_change_risk": "high",
            },
            "anvil_extras_orm": {
                "indicators": [
                    r"from anvil_extras\.orm",
                    r"@anvil_extras\.orm\.model_class",
                    r"\.Model\.",
                ],
                "description": "Anvil Extras ORM (legacy)",
                "modern_alternative": "Official Anvil model classes",
                "migration_complexity": "moderate",
                "breaking_change_risk": "medium",
            },
            "custom_theme_integration": {
                "indicators": [
                    r"@theme\.[a-zA-Z_]+\s*=",
                    r"anvil\.html\.theme\s*=",
                    r"custom_css\s*=",
                    r"<style>",
                ],
                "description": "Custom theme integration",
                "modern_alternative": "Material 3 theme system",
                "migration_complexity": "complex",
                "breaking_change_risk": "medium",
            },
            "manual_state_management": {
                "indicators": [
                    r"global\s+\w+",
                    r"__init__\s*=\s*{",
                    r"self\._state\s*=",
                    r"manual_refresh\s*=\s*True",
                ],
                "exclusions": [
                    r"@reactive_class",
                    r"signal\(",
                    r"reactive_instance\(",
                ],
                "description": "Manual state management without reactive patterns",
                "modern_alternative": "Reactive library with signals",
                "migration_complexity": "moderate",
                "breaking_change_risk": "low",
            },
            "legacy_form_navigation": {
                "indicators": [
                    r"anvil\.open_form\(",
                    r"anvil\.get_open_form\(\)",
                    r"self\.parent_form",
                    r"form\.show\(\)",
                ],
                "exclusions": [
                    r"anvil\.routing\.",
                    r"routing_context",
                ],
                "description": "Legacy form-based navigation",
                "modern_alternative": "Official routing system",
                "migration_complexity": "moderate",
                "breaking_change_risk": "medium",
            },
            "panel_based_layouts": {
                "indicators": [
                    r"\.add_component\(",
                    r"\.clear\(\)",
                    r"ColumnPanel\(",
                    r"FlowPanel\(",
                    r"GridPanel\(",
                ],
                "exclusions": [
                    r"NavigationDrawerLayout",
                    r"NavigationRailLayout",
                    r"LinearPanel",
                ],
                "description": "Panel-based layout architecture",
                "modern_alternative": "Modern layout system",
                "migration_complexity": "moderate",
                "breaking_change_risk": "medium",
            },
            "inline_event_handlers": {
                "indicators": [
                    r"onclick\s*=\s*['\"][^'\"]*['\"]",
                    r"on_change\s*=\s*['\"][^'\"]*['\"]",
                    r"on_show\s*=\s*['\"][^'\"]*['\"]",
                ],
                "description": "Inline event handlers in YAML",
                "modern_alternative": "Python method event handlers",
                "migration_complexity": "simple",
                "breaking_change_risk": "low",
            },
            "hardcoded_ui_properties": {
                "indicators": [
                    r"background_color\s*=\s*['\"]#[0-9a-fA-F]{6}['\"]",
                    r"foreground_color\s*=\s*['\"]#[0-9a-fA-F]{6}['\"]",
                    r"font_size\s*=\s*\d+",
                    r"border\s*=\s*\d+",
                ],
                "description": "Hardcoded UI properties",
                "modern_alternative": "Theme-based styling",
                "migration_complexity": "simple",
                "breaking_change_risk": "low",
            },
        }

        # Mixed architecture patterns
        self._mixed_architecture_patterns = {
            "hybrid_routing": {
                "combinations": [
                    (r"from anvil_extras\.routing", r"anvil\.routing\."),
                    (r"@anvil_extras\.routing\.route", r"@anvil\.routing\.route"),
                ],
                "description": "Mixed routing systems",
                "complexity": "high",
                "risks": ["route_conflicts", "inconsistent_behavior"],
            },
            "partial_reactive": {
                "combinations": [
                    (r"@reactive_class", r"global\s+\w+"),
                    (r"signal\(", r"manual_refresh\s*=\s*True"),
                ],
                "description": "Partial reactive implementation",
                "complexity": "moderate",
                "risks": ["inconsistent_state", "performance_issues"],
            },
            "component_mismatch": {
                "combinations": [
                    (r"NavigationDrawerLayout", r"ColumnPanel.*add_component"),
                    (r"@theme\.", r"background_color\s*="),
                ],
                "description": "Mixed component patterns",
                "complexity": "moderate",
                "risks": ["ui_inconsistency", "theme_conflicts"],
            },
        }

        # Anti-patterns that should be flagged
        self._anti_patterns = {
            "deeply_nested_panels": {
                "pattern": r"(\s{16,}|\t{4,})ColumnPanel|FlowPanel|GridPanel",
                "description": "Deeply nested panel hierarchy",
                "impact": "performance_maintainability",
                "severity": "medium",
            },
            "global_state_abuse": {
                "pattern": r"global\s+\w+.*\n.*global\s+\w+",
                "description": "Excessive use of global variables",
                "impact": "maintainability_testing",
                "severity": "high",
            },
            "inline_styling": {
                "pattern": r"style\s*=\s*['\"][^'\"]*color:[^'\"]*['\"]",
                "description": "Inline CSS styling",
                "impact": "maintainability_consistency",
                "severity": "low",
            },
            "synchronous_server_calls": {
                "pattern": r"anvil\.server\.call\([^)]*\)(?!\s*\.then\(|\s*\.catch\()",
                "description": "Synchronous server calls without error handling",
                "impact": "user_experience_reliability",
                "severity": "high",
            },
        }

    def detect_anvil_patterns(self, project_path: Path) -> Dict[str, Any]:
        """
        Comprehensive pattern detection for Anvil v1.0+ projects.

        Args:
            project_path: Path to Anvil project

        Returns:
            Comprehensive pattern analysis results
        """
        if not self.project_manager.is_anvil_project(project_path):
            raise ValueError(f"Not an Anvil project: {project_path}")

        # Get all relevant files
        client_files = self.project_manager.get_project_files(
            project_path, "client_code/**/*"
        )
        server_files = self.project_manager.get_project_files(
            project_path, "server_code/**/*"
        )
        theme_files = self.project_manager.get_project_files(project_path, "theme/**/*")
        form_files = self.project_manager.get_project_files(project_path, "**/*.yaml")

        all_files = client_files + server_files + theme_files + form_files

        # Detect patterns
        legacy_patterns = self._detect_legacy_patterns(all_files)
        mixed_architectures = self._detect_mixed_architectures(all_files)
        anti_patterns = self._detect_anti_patterns(all_files)
        compatibility_matrix = self._build_compatibility_matrix(
            legacy_patterns, mixed_architectures
        )

        # Analyze migration opportunities
        migration_opportunities = self._analyze_migration_opportunities(
            legacy_patterns, mixed_architectures
        )

        # Calculate risk scores
        risk_assessment = self._calculate_risk_assessment(
            legacy_patterns, mixed_architectures, anti_patterns
        )

        return {
            "legacy_patterns": legacy_patterns,
            "mixed_architectures": mixed_architectures,
            "anti_patterns": anti_patterns,
            "compatibility_matrix": compatibility_matrix,
            "migration_opportunities": migration_opportunities,
            "risk_assessment": risk_assessment,
            "analysis_summary": self._generate_analysis_summary(
                legacy_patterns, mixed_architectures, anti_patterns
            ),
        }

    def _detect_legacy_patterns(self, files: List[Path]) -> List[LegacyPattern]:
        """Detect legacy patterns in project files."""
        detected_patterns = []

        for file_path in files:
            try:
                content = self.file_system.read_text(file_path)
                lines = content.split("\n")

                for pattern_name, pattern_info in self._legacy_patterns.items():
                    indicators = pattern_info["indicators"]
                    exclusions = pattern_info.get("exclusions", [])
                    matches = self._find_pattern_matches(
                        content, indicators, exclusions
                    )

                    if matches:
                        line_numbers = [
                            i + 1
                            for i, line in enumerate(lines)
                            if any(
                                re.search(indicator, line) for indicator in indicators
                            )
                        ]

                        severity = self._calculate_pattern_severity(
                            pattern_name, len(matches), file_path.suffix
                        )

                        pattern = LegacyPattern(
                            name=pattern_name,
                            category=self._categorize_pattern(pattern_name),
                            description=pattern_info["description"],
                            file_path=file_path,
                            line_numbers=line_numbers,
                            severity=severity,
                            migration_complexity=pattern_info["migration_complexity"],
                            modern_alternative=pattern_info["modern_alternative"],
                            breaking_change_risk=pattern_info["breaking_change_risk"],
                        )

                        detected_patterns.append(pattern)

            except Exception:
                # Log error but continue with other files
                continue

        return detected_patterns

    def _detect_mixed_architectures(self, files: List[Path]) -> List[Dict[str, Any]]:
        """Detect mixed architecture patterns."""
        mixed_patterns = []

        for file_path in files:
            try:
                content = self.file_system.read_text(file_path)

                for (
                    pattern_name,
                    pattern_info,
                ) in self._mixed_architecture_patterns.items():
                    for combo1, combo2 in pattern_info["combinations"]:
                        if re.search(combo1, content) and re.search(combo2, content):
                            mixed_patterns.append(
                                {
                                    "name": pattern_name,
                                    "description": pattern_info["description"],
                                    "file_path": file_path,
                                    "complexity": pattern_info["complexity"],
                                    "risks": pattern_info["risks"],
                                    "patterns_found": [combo1, combo2],
                                }
                            )

            except Exception:
                continue

        return mixed_patterns

    def _detect_anti_patterns(self, files: List[Path]) -> List[Dict[str, Any]]:
        """Detect anti-patterns that should be addressed."""
        anti_patterns_found = []

        for file_path in files:
            try:
                content = self.file_system.read_text(file_path)

                for pattern_name, pattern_info in self._anti_patterns.items():
                    matches = list(re.finditer(pattern_info["pattern"], content))

                    if matches:
                        line_numbers = [
                            content[: match.start()].count("\n") + 1
                            for match in matches
                        ]

                        anti_patterns_found.append(
                            {
                                "name": pattern_name,
                                "description": pattern_info["description"],
                                "file_path": file_path,
                                "line_numbers": line_numbers,
                                "impact": pattern_info["impact"],
                                "severity": pattern_info["severity"],
                                "match_count": len(matches),
                            }
                        )

            except Exception:
                continue

        return anti_patterns_found

    def _build_compatibility_matrix(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
    ) -> List[PatternCompatibility]:
        """Build compatibility matrix for detected patterns."""
        compatibility_matrix = []

        # Check compatibility between legacy patterns
        for i, pattern1 in enumerate(legacy_patterns):
            for pattern2 in legacy_patterns[i + 1 :]:
                compatibility = self._assess_pattern_compatibility(pattern1, pattern2)
                compatibility_matrix.append(compatibility)

        # Check compatibility with mixed architectures
        for mixed_pattern in mixed_architectures:
            for legacy_pattern in legacy_patterns:
                compatibility = self._assess_mixed_compatibility(
                    mixed_pattern, legacy_pattern
                )
                compatibility_matrix.append(compatibility)

        return compatibility_matrix

    def _analyze_migration_opportunities(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
    ) -> Dict[str, Any]:
        """Analyze migration opportunities and prioritize them."""
        opportunities = {
            "quick_wins": [],
            "moderate_effort": [],
            "strategic_initiatives": [],
            "risk_mitigation": [],
        }

        # Categorize by complexity and impact
        for pattern in legacy_patterns:
            opportunity = {
                "pattern": pattern.name,
                "description": pattern.description,
                "files_affected": [pattern.file_path],
                "complexity": pattern.migration_complexity,
                "impact": self._calculate_pattern_impact(pattern),
                "modern_alternative": pattern.modern_alternative,
                "breaking_change_risk": pattern.breaking_change_risk,
            }

            if pattern.migration_complexity == "simple":
                opportunities["quick_wins"].append(opportunity)
            elif pattern.migration_complexity == "moderate":
                opportunities["moderate_effort"].append(opportunity)
            else:
                opportunities["strategic_initiatives"].append(opportunity)

        # Add risk mitigation opportunities
        for mixed_pattern in mixed_architectures:
            if mixed_pattern["complexity"] == "high":
                opportunities["risk_mitigation"].append(
                    {
                        "pattern": mixed_pattern["name"],
                        "description": mixed_pattern["description"],
                        "risks": mixed_pattern["risks"],
                        "priority": "high",
                    }
                )

        return opportunities

    def _calculate_risk_assessment(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
        anti_patterns: List[Dict[str, Any]],
    ) -> Dict[str, Any]:
        """Calculate overall risk assessment."""
        risk_scores = {
            "breaking_change_risk": 0.0,
            "performance_risk": 0.0,
            "maintainability_risk": 0.0,
            "security_risk": 0.0,
        }

        # Risk from legacy patterns
        for pattern in legacy_patterns:
            if pattern.breaking_change_risk == "high":
                risk_scores["breaking_change_risk"] += 0.3
            elif pattern.breaking_change_risk == "medium":
                risk_scores["breaking_change_risk"] += 0.1

            if pattern.migration_complexity == "complex":
                risk_scores["maintainability_risk"] += 0.2
            elif pattern.migration_complexity == "expert":
                risk_scores["maintainability_risk"] += 0.3

        # Risk from mixed architectures
        for mixed_pattern in mixed_architectures:
            if mixed_pattern["complexity"] == "high":
                risk_scores["performance_risk"] += 0.2
                risk_scores["maintainability_risk"] += 0.2

        # Risk from anti-patterns
        for anti_pattern in anti_patterns:
            if anti_pattern["severity"] == "high":
                risk_scores["security_risk"] += 0.2
                risk_scores["performance_risk"] += 0.1
            elif anti_pattern["severity"] == "medium":
                risk_scores["maintainability_risk"] += 0.1

        # Normalize scores to 0-1 range
        for key in risk_scores:
            risk_scores[key] = min(risk_scores[key], 1.0)

        # Calculate overall risk
        risk_scores["overall_risk"] = sum(risk_scores.values()) / len(risk_scores)

        return risk_scores

    def _generate_analysis_summary(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
        anti_patterns: List[Dict[str, Any]],
    ) -> Dict[str, Any]:
        """Generate summary of the analysis."""
        return {
            "total_legacy_patterns": len(legacy_patterns),
            "total_mixed_architectures": len(mixed_architectures),
            "total_anti_patterns": len(anti_patterns),
            "high_priority_issues": len(
                [p for p in legacy_patterns if p.severity in ["high", "critical"]]
            )
            + len([a for a in anti_patterns if a["severity"] == "high"]),
            "modernisation_readiness": self._calculate_modernisation_readiness(
                legacy_patterns, mixed_architectures, anti_patterns
            ),
            "recommended_approach": self._recommend_modernisation_approach(
                legacy_patterns, mixed_architectures
            ),
        }

    def _find_pattern_matches(
        self, content: str, indicators: List[str], exclusions: List[str]
    ) -> List[str]:
        """Find pattern matches in content."""
        matches = []

        for indicator in indicators:
            if re.search(indicator, content):
                # Check exclusions
                excluded = any(
                    re.search(exclusion, content) for exclusion in exclusions
                )
                if not excluded:
                    matches.append(indicator)

        return matches

    def _calculate_pattern_severity(
        self, pattern_name: str, match_count: int, file_extension: str
    ) -> str:
        """Calculate severity of a detected pattern."""
        base_severity = {
            "raw_table_access": "high",
            "anvil_extras_routing": "medium",
            "anvil_extras_orm": "medium",
            "manual_state_management": "medium",
            "legacy_form_navigation": "medium",
            "panel_based_layouts": "low",
            "classic_components_only": "low",
            "custom_theme_integration": "medium",
            "inline_event_handlers": "low",
            "hardcoded_ui_properties": "low",
        }

        severity = base_severity.get(pattern_name, "low")

        # Upgrade severity based on frequency
        if match_count > 10:
            severity_levels = {"low": "medium", "medium": "high", "high": "critical"}
            severity = severity_levels.get(severity, "critical")

        return severity

    def _categorize_pattern(self, pattern_name: str) -> str:
        """Categorize a pattern into a broader category."""
        categories = {
            "anvil_extras_routing": "routing",
            "classic_components_only": "ui_components",
            "raw_table_access": "data_layer",
            "anvil_extras_orm": "data_layer",
            "custom_theme_integration": "theming",
            "manual_state_management": "state_management",
            "legacy_form_navigation": "routing",
            "panel_based_layouts": "layout",
            "inline_event_handlers": "event_handling",
            "hardcoded_ui_properties": "styling",
        }

        return categories.get(pattern_name, "general")

    def _assess_pattern_compatibility(
        self, pattern1: LegacyPattern, pattern2: LegacyPattern
    ) -> PatternCompatibility:
        """Assess compatibility between two patterns."""
        # Default compatibility
        compatibility_score = 0.7
        conflicts = []
        synergies = []

        # Specific compatibility rules
        if pattern1.category == pattern2.category:
            compatibility_score -= 0.2
            conflicts.append(f"Same category: {pattern1.category}")

        if (
            pattern1.breaking_change_risk == "high"
            and pattern2.breaking_change_risk == "high"
        ):
            compatibility_score -= 0.3
            conflicts.append("Both high breaking change risk")

        # Synergies
        if (
            pattern1.modern_alternative == pattern2.modern_alternative
            and pattern1.modern_alternative is not None
        ):
            synergies.append(
                f"Shared modern alternative: {pattern1.modern_alternative}"
            )
            compatibility_score += 0.1

        return PatternCompatibility(
            pattern1=pattern1.name,
            pattern2=pattern2.name,
            compatibility_score=max(0.0, min(1.0, compatibility_score)),
            conflicts=conflicts,
            synergies=synergies,
        )

    def _assess_mixed_compatibility(
        self, mixed_pattern: Dict[str, Any], legacy_pattern: LegacyPattern
    ) -> PatternCompatibility:
        """Assess compatibility between mixed architecture and legacy pattern."""
        compatibility_score = 0.5  # Start lower for mixed patterns
        conflicts = mixed_pattern.get("risks", [])
        synergies = []

        if mixed_pattern["complexity"] == "high":
            compatibility_score -= 0.2

        return PatternCompatibility(
            pattern1=mixed_pattern["name"],
            pattern2=legacy_pattern.name,
            compatibility_score=max(0.0, min(1.0, compatibility_score)),
            conflicts=conflicts,
            synergies=synergies,
        )

    def _calculate_pattern_impact(self, pattern: LegacyPattern) -> str:
        """Calculate the impact of migrating a pattern."""
        impact_factors = {
            "raw_table_access": "high",
            "anvil_extras_routing": "medium",
            "anvil_extras_orm": "medium",
            "manual_state_management": "medium",
            "legacy_form_navigation": "medium",
            "panel_based_layouts": "high",
            "classic_components_only": "medium",
            "custom_theme_integration": "low",
            "inline_event_handlers": "low",
            "hardcoded_ui_properties": "low",
        }

        return impact_factors.get(pattern.name, "medium")

    def _calculate_modernisation_readiness(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
        anti_patterns: List[Dict[str, Any]],
    ) -> float:
        """Calculate how ready the project is for modernisation."""
        # Start with base score
        readiness = 0.8

        # Deduct for legacy patterns
        readiness -= len(legacy_patterns) * 0.05

        # Deduct more for mixed architectures
        readiness -= len(mixed_architectures) * 0.1

        # Deduct for anti-patterns
        high_severity_anti = len([a for a in anti_patterns if a["severity"] == "high"])
        readiness -= high_severity_anti * 0.1

        return max(0.0, min(1.0, readiness))

    def _recommend_modernisation_approach(
        self,
        legacy_patterns: List[LegacyPattern],
        mixed_architectures: List[Dict[str, Any]],
    ) -> str:
        """Recommend the best modernisation approach."""
        high_complexity = len(
            [
                p
                for p in legacy_patterns
                if p.migration_complexity in ["complex", "expert"]
            ]
        )

        mixed_count = len(mixed_architectures)

        if mixed_count > 2 or high_complexity > 3:
            return "incremental_migration"
        elif mixed_count > 0 or high_complexity > 0:
            return "hybrid_approach"
        else:
            return "modern_rewrite"
