"""Connect code quality issues to business impact.

Combines technical metrics (complexity, coupling, duplication) with
business signals (incident frequency, delivery velocity, code churn)
to produce a unified business impact score for each module/file.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any


@dataclass
class BusinessImpact:
    """Business impact assessment for a code module."""

    file_path: str
    technical_debt_score: float  # 0-100, higher = more debt
    business_impact_score: float  # 0-100, higher = more impact
    combined_priority: float  # 0-100
    risk_level: str  # "critical", "high", "medium", "low"
    factors: dict[str, float]  # Breakdown of what contributes

    def to_dict(self) -> dict[str, Any]:
        return {
            "file_path": self.file_path,
            "technical_debt_score": round(self.technical_debt_score, 1),
            "business_impact_score": round(self.business_impact_score, 1),
            "combined_priority": round(self.combined_priority, 1),
            "risk_level": self.risk_level,
            "factors": {k: round(v, 2) for k, v in self.factors.items()},
        }


class ImpactScorer:
    """Score technical debt items by business impact.

    Weights:
    - Complexity contributes to maintenance cost
    - Coupling increases blast radius of changes
    - Duplication multiplies bug fix effort
    - Incident history shows real-world impact
    - Low test coverage increases deployment risk
    - Poor documentation slows onboarding
    """

    # Default weights for each factor (sum to 1.0)
    DEFAULT_WEIGHTS = {
        "complexity": 0.20,
        "coupling": 0.15,
        "duplication": 0.10,
        "dead_code": 0.05,
        "test_gaps": 0.15,
        "doc_gaps": 0.05,
        "incidents": 0.20,
        "velocity_impact": 0.10,
    }

    def __init__(self, weights: dict[str, float] | None = None) -> None:
        self.weights = weights or self.DEFAULT_WEIGHTS.copy()

    def score_file(
        self,
        file_path: str,
        complexity_score: float = 0,
        coupling_score: float = 0,
        duplication_score: float = 0,
        dead_code_score: float = 0,
        test_gap_score: float = 0,
        doc_gap_score: float = 0,
        incident_count: int = 0,
        velocity_ratio: float = 1.0,
    ) -> BusinessImpact:
        """Calculate business impact for a single file.

        All scores should be normalized to 0-100 range.

        Args:
            file_path: Path to the file being scored.
            complexity_score: Normalized complexity (0-100).
            coupling_score: Normalized coupling (0-100).
            duplication_score: Normalized duplication (0-100).
            dead_code_score: Normalized dead code amount (0-100).
            test_gap_score: Percentage of untested public APIs (0-100).
            doc_gap_score: Percentage of undocumented public APIs (0-100).
            incident_count: Number of incidents linked to this file.
            velocity_ratio: Delivery ratio (1.0 = on track, <1 = slowed).
        """
        # Normalize incident count to 0-100 (cap at 10 incidents = 100)
        incident_score = min(100.0, incident_count * 10.0)

        # Velocity impact: 1.0 = no impact, 0.5 = 50% slower
        velocity_score = max(0.0, min(100.0, (1.0 - velocity_ratio) * 100.0))

        factors = {
            "complexity": min(100.0, complexity_score),
            "coupling": min(100.0, coupling_score),
            "duplication": min(100.0, duplication_score),
            "dead_code": min(100.0, dead_code_score),
            "test_gaps": min(100.0, test_gap_score),
            "doc_gaps": min(100.0, doc_gap_score),
            "incidents": incident_score,
            "velocity_impact": velocity_score,
        }

        # Technical debt score: raw technical quality issues
        tech_factors = ["complexity", "coupling", "duplication", "dead_code", "test_gaps", "doc_gaps"]
        tech_weights = {k: self.weights[k] for k in tech_factors}
        tech_total = sum(tech_weights.values()) or 1.0
        technical_debt_score = sum(
            factors[k] * (tech_weights[k] / tech_total) for k in tech_factors
        )

        # Business impact: how much this affects the business
        biz_factors = ["incidents", "velocity_impact", "test_gaps", "coupling"]
        biz_weights = {k: self.weights[k] for k in biz_factors}
        biz_total = sum(biz_weights.values()) or 1.0
        business_impact_score = sum(
            factors[k] * (biz_weights[k] / biz_total) for k in biz_factors
        )

        # Combined priority: geometric mean gives higher weight when both are high
        combined = (technical_debt_score * 0.4 + business_impact_score * 0.6)

        risk_level = self._classify_risk(combined)

        return BusinessImpact(
            file_path=file_path,
            technical_debt_score=technical_debt_score,
            business_impact_score=business_impact_score,
            combined_priority=combined,
            risk_level=risk_level,
            factors=factors,
        )

    def score_files(
        self, file_metrics: list[dict[str, Any]]
    ) -> list[BusinessImpact]:
        """Score multiple files and return sorted by priority.

        Args:
            file_metrics: List of dicts with keys matching score_file params.
        """
        results = []
        for metrics in file_metrics:
            fp = metrics.pop("file_path", "unknown")
            impact = self.score_file(file_path=fp, **metrics)
            results.append(impact)

        results.sort(key=lambda x: x.combined_priority, reverse=True)
        return results

    def _classify_risk(self, score: float) -> str:
        """Classify risk level from combined score."""
        if score >= 75:
            return "critical"
        elif score >= 50:
            return "high"
        elif score >= 25:
            return "medium"
        return "low"
