"""Improvement detection -- compares runs to detect regressions and gains."""

from __future__ import annotations

from dataclasses import dataclass


@dataclass
class Comparison:
    """Result of comparing a single metric between two runs."""

    metric_name: str
    previous_value: float
    current_value: float
    delta: float
    improved: bool
    regression: bool

    def to_dict(self) -> dict:
        """Serialize to dictionary."""
        return {
            "metric": self.metric_name,
            "previous": round(self.previous_value, 4),
            "current": round(self.current_value, 4),
            "delta": round(self.delta, 4),
            "improved": self.improved,
            "regression": self.regression,
        }


class ImprovementDetector:
    """Compares discovery runs to detect improvement or regression."""

    HIGHER_IS_BETTER = frozenset(
        {"edge_precision", "edge_recall", "edge_f1", "orientation_accuracy"}
    )
    LOWER_IS_BETTER = frozenset({"shd", "execution_time_ms"})

    def __init__(self, tolerance: float = 0.01) -> None:
        self._tolerance = tolerance

    def compare(self, previous: object, current: object) -> list[Comparison]:
        """Compare two DiscoveryMetrics runs across all tracked metrics."""
        comparisons = []
        metrics = sorted(self.HIGHER_IS_BETTER | self.LOWER_IS_BETTER)

        for metric in metrics:
            prev_val = getattr(previous, metric, 0)
            curr_val = getattr(current, metric, 0)
            delta = curr_val - prev_val

            if metric in self.HIGHER_IS_BETTER:
                improved = delta > self._tolerance
                regression = delta < -self._tolerance
            else:
                improved = delta < -self._tolerance
                regression = delta > self._tolerance

            comparisons.append(
                Comparison(
                    metric_name=metric,
                    previous_value=float(prev_val),
                    current_value=float(curr_val),
                    delta=float(delta),
                    improved=improved,
                    regression=regression,
                )
            )
        return comparisons

    def has_regression(self, previous: object, current: object) -> bool:
        """Check if any metric regressed beyond tolerance."""
        return any(c.regression for c in self.compare(previous, current))

    def improvement_summary(self, previous: object, current: object) -> dict:
        """Produce a categorized summary of all metric changes."""
        comps = self.compare(previous, current)
        return {
            "improved": [c.to_dict() for c in comps if c.improved],
            "regressed": [c.to_dict() for c in comps if c.regression],
            "unchanged": [c.to_dict() for c in comps if not c.improved and not c.regression],
            "has_regression": any(c.regression for c in comps),
            "net_improvements": sum(1 for c in comps if c.improved)
            - sum(1 for c in comps if c.regression),
        }
