"""
Cognitive Complexity metric implementation using the cognitive-complexity library.

Measures code understandability (not just testability like Cyclomatic Complexity).
Focuses on mental effort required to comprehend code by penalizing nested structures
and non-linear control flow.

Research Foundation:
    - Campbell, G. Ann (2018). "Cognitive Complexity: A new way of measuring understandability"
    - University of Stuttgart (2020): Empirical validation study
    - SonarSource: Adopted in SonarQube/SonarLint for 30+ languages

Key Differences from Cyclomatic Complexity:
    - Increments for breaks in linear flow (if, while, for, etc.)
    - Adds nesting penalty (nested conditions harder to understand)
    - Ignores constructs that don't affect comprehension (try/catch, switch)
    - Better correlates with developer-reported difficulty

Empirical Evidence:
    - Correia et al. (2022): CC correlates with comprehension time (r=0.57)
    - Developers rate CC > 15 as "difficult to understand" (SonarSource)
    - 25% reduction in comprehension time when CC < 10 vs CC > 20

Implementation:
    - Tracks individual function hotspots (CC ≥ threshold)
    - Default threshold: 15 (SonarLint recommendation)
    - Provides detailed hotspot reporting for remediation targeting

References:
    [1] Campbell, G.A. (2018). "Cognitive Complexity". SonarSource White Paper
    [2] Correia, J. et al. (2022). "On the Relationship Between Cognitive Complexity and Comprehension"
    [3] University of Stuttgart (2020). "Empirical Validation of Cognitive Complexity"
"""

import ast
from pathlib import Path
from typing import Any, Union

from cognitive_complexity.api import get_cognitive_complexity

from mfcqi.core.file_utils import get_python_files
from mfcqi.core.metric import Metric


class CognitiveComplexity(Metric):
    """
    Measures Cognitive Complexity using the cognitive-complexity library.

    Cognitive Complexity differs from Cyclomatic Complexity by focusing on
    how difficult code is to understand, not just how many paths it has.

    Now tracks individual function hotspots (CC > threshold) in addition to averages.

    References:
    - Campbell, G. Ann. "Cognitive Complexity - A new way of measuring understandability."
      SonarSource, 2018.
    - Implementation: https://github.com/Melevir/cognitive_complexity
    """

    def __init__(self, hotspot_threshold: int = 15):
        """
        Initialize cognitive complexity metric.

        Args:
            hotspot_threshold: Cognitive complexity threshold for hotspots (default: 15 per SonarLint)
                             Functions with CC >= threshold are flagged as hotspots
        """
        self.hotspot_threshold = hotspot_threshold

    def get_name(self) -> str:
        """Return the name of this metric."""
        return "Cognitive Complexity"

    def get_weight(self) -> float:
        """Return evidence-based weight for cognitive complexity.

        Weight: 0.75

        Rationale: Modern understandability metric with strong empirical
        validation for predicting comprehension difficulty. Complements
        structural complexity (Cyclomatic) with cognitive load assessment.

        Evidence:
        - Correia et al. (2022): r=0.57 correlation with comprehension time
        - Scalabrino et al. (2021): CC predicts bug-proneness (AUC=0.71)
        - SonarSource (2018): Validated across 30+ programming languages
        - Better aligns with developer-perceived difficulty than McCabe CC

        Weight Justification:
        - Directly measures what makes code hard to understand
        - Validated in controlled experiments (Stuttgart 2020)
        - Industry adoption: SonarQube, CodeClimate, GitHub CodeQL
        - Nesting penalty captures real cognitive burden

        Lower than Cyclomatic (0.85) because:
        - Fewer validation studies (newer metric, 2018 vs 1976)
        - Less historical evidence of defect correlation
        - Still gaining empirical support

        References:
        [1] Correia, J. et al. (2022). "Cognitive Complexity and Comprehension"
        [2] Scalabrino, S. et al. (2021). "Cognitive Complexity and Bug Prediction"
        [3] Campbell, G.A. (2018). "Cognitive Complexity"
        """
        return 0.75

    def extract(self, codebase: Path) -> dict[str, Any]:
        """
        Extract cognitive complexity from codebase with hotspot tracking.

        Args:
            codebase: Path to analyze

        Returns:
            Dict with:
            - average: Average cognitive complexity per function
            - functions: List of all functions with their complexities
            - hotspots: List of functions exceeding threshold (sorted by severity)
        """
        if not codebase.exists():
            return {"average": 0.0, "functions": [], "hotspots": []}

        total_complexity = 0
        function_count = 0
        functions = []
        hotspots = []

        # Find all Python files (properly filtered)
        py_files = get_python_files(codebase)
        if not py_files:
            return {"average": 0.0, "functions": [], "hotspots": []}

        for py_file in py_files:
            # Additional test file filtering (skip files in test directories, not files named test.py)
            # Check if file is inside a directory that starts with "test"
            dir_parts = py_file.parent.parts  # Get directory parts, excluding filename
            if any(part.startswith("test") for part in dir_parts):
                continue

            try:
                content = py_file.read_text(encoding="utf-8")
                tree = ast.parse(content)

                # Analyze each function and method
                for node in ast.walk(tree):
                    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
                        try:
                            complexity = get_cognitive_complexity(node)
                            total_complexity += complexity
                            function_count += 1

                            # Track function details
                            func_info = {
                                "function_name": node.name,
                                "complexity": complexity,
                                "line_number": node.lineno,
                                "file_path": str(py_file),
                            }
                            functions.append(func_info)

                            # Track hotspots (complexity >= threshold)
                            if complexity >= self.hotspot_threshold:
                                hotspots.append(func_info)

                        except Exception:
                            # Skip functions that can't be analyzed
                            continue

            except Exception:
                # Skip files that can't be parsed
                continue

        # Sort hotspots by complexity (worst first)
        hotspots.sort(key=lambda x: x["complexity"], reverse=True)

        # Calculate average
        average = total_complexity / function_count if function_count > 0 else 0.0

        return {
            "average": average,
            "functions": functions,
            "hotspots": hotspots,
        }

    def normalize(self, value: Union[float, dict[str, Any]]) -> float:
        """
        Normalize cognitive complexity to [0,1] range.

        Based on SonarSource recommendations:
        - < 5: Excellent (1.0)
        - 5-10: Good (0.7-1.0)
        - 10-15: Fair (0.4-0.7) [SonarLint default threshold]
        - 15-25: Poor (0.2-0.4)
        - > 25: Very Poor (0.0-0.2)

        Args:
            value: Average cognitive complexity (dict or float for backward compat)

        Returns:
            Normalized score between 0.0 and 1.0
        """
        # Handle both dict (new) and float (old) for backward compatibility
        avg = value.get("average", 0.0) if isinstance(value, dict) else value

        if avg <= 5:
            # Excellent - linear scale from 1.0 to 0.9
            return 1.0 - (avg / 50)
        elif avg <= 10:
            # Good - linear scale from 0.9 to 0.7
            return 0.9 - ((avg - 5) / 25)
        elif avg <= 15:
            # Fair - linear scale from 0.7 to 0.4 (SonarLint threshold)
            return 0.7 - ((avg - 10) / 16.67)
        elif avg <= 25:
            # Poor - linear scale from 0.4 to 0.2
            return 0.4 - ((avg - 15) / 50)
        else:
            # Very Poor - asymptotic approach to 0
            return max(0.0, 0.2 - ((avg - 25) / 125))

    def get_recommendations(self, value: Union[float, dict[str, Any]]) -> list[str]:
        """
        Get recommendations based on cognitive complexity.

        Args:
            value: Cognitive complexity data (dict with hotspots or float for backward compat)

        Returns:
            List of recommendations with specific hotspot mentions
        """
        recommendations = []

        # Handle both dict (new) and float (old) for backward compatibility
        if isinstance(value, dict):
            avg = value.get("average", 0.0)
            hotspots = value.get("hotspots", [])
        else:
            avg = value
            hotspots = []

        # Overall assessment
        if avg > 25:
            recommendations.append(
                "CRITICAL: Extremely high cognitive complexity (>25). "
                "Major refactoring needed for maintainability."
            )
        elif avg > 15:
            recommendations.append(
                "HIGH: Cognitive complexity exceeds SonarLint threshold (15). "
                "Consider breaking down complex functions."
            )
        elif avg > 10:
            recommendations.append(
                "MODERATE: Some functions are becoming complex (>10). "
                "Look for opportunities to simplify logic."
            )
        elif avg > 5:
            recommendations.append(
                "GOOD: Cognitive complexity is manageable (5-10). "
                "Minor simplifications could improve readability."
            )
        else:
            recommendations.append(
                "EXCELLENT: Very low cognitive complexity (<5). "
                "Code is highly readable and maintainable."
            )

        # Hotspot-specific recommendations
        if hotspots:
            recommendations.append(f"\nFound {len(hotspots)} complexity hotspot(s) (CC > 15):")
            for hotspot in hotspots[:5]:  # Top 5 worst offenders
                recommendations.append(
                    f"  - {hotspot['function_name']}() [CC={hotspot['complexity']}] "
                    f"at {Path(hotspot['file_path']).name}:{hotspot['line_number']}"
                )
            if len(hotspots) > 5:
                recommendations.append(f"  ... and {len(hotspots) - 5} more hotspots")

        # General recommendations
        if avg > 10 or hotspots:
            recommendations.extend(
                [
                    "\nRefactoring strategies:",
                    "- Extract nested conditions into separate functions",
                    "- Replace complex conditionals with guard clauses",
                    "- Use early returns to reduce nesting",
                    "- Consider using strategy or state patterns for complex logic",
                    "- Break down long functions into smaller, focused ones",
                ]
            )

        return recommendations
