"""
Version comparator for releaseops analytics.

Compares behavioral metrics between two bundle versions to detect
regressions, improvements, and generate recommendations.
"""

from __future__ import annotations

from typing import List

from llmhq_releaseops.analytics.models import (
    BehaviorMetrics,
    MetricChange,
    VersionComparison,
)


class VersionComparator:
    """Compares metrics between two bundle versions."""

    COMPARABLE_METRICS = [
        "error_rate",
        "avg_latency_ms",
        "p95_latency_ms",
        "p99_latency_ms",
        "avg_token_usage",
        "success_rate",
    ]

    def compare(
        self, baseline: BehaviorMetrics, candidate: BehaviorMetrics
    ) -> VersionComparison:
        """
        Compare baseline and candidate metrics.

        Produces per-metric changes, overall assessment, and recommendation.
        """
        metric_changes = []

        for metric_name in self.COMPARABLE_METRICS:
            baseline_val = getattr(baseline, metric_name, 0.0)
            candidate_val = getattr(candidate, metric_name, 0.0)
            change = self._compute_change(metric_name, baseline_val, candidate_val)
            metric_changes.append(change)

        significant = [c for c in metric_changes if c.significance != "negligible"]
        assessment = self._assess_overall(metric_changes)
        recommendation = self._generate_recommendation(assessment, significant)

        return VersionComparison(
            baseline=baseline,
            candidate=candidate,
            metric_changes=metric_changes,
            significant_changes=significant,
            overall_assessment=assessment,
            recommendation=recommendation,
        )

    def _compute_change(
        self, metric_name: str, baseline_val: float, candidate_val: float
    ) -> MetricChange:
        """Compute the change for a single metric."""
        absolute_change = candidate_val - baseline_val

        if baseline_val == 0:
            percent_change = 0.0 if candidate_val == 0 else 100.0
        else:
            percent_change = (absolute_change / abs(baseline_val)) * 100

        if absolute_change > 0:
            direction = "increase"
        elif absolute_change < 0:
            direction = "decrease"
        else:
            direction = "no_change"

        abs_pct = abs(percent_change)
        if abs_pct > 25:
            significance = "major"
        elif abs_pct > 10:
            significance = "moderate"
        elif abs_pct > 5:
            significance = "minor"
        else:
            significance = "negligible"

        return MetricChange(
            metric_name=metric_name,
            baseline_value=baseline_val,
            candidate_value=candidate_val,
            absolute_change=absolute_change,
            percent_change=percent_change,
            direction=direction,
            significance=significance,
        )

    def _assess_overall(self, changes: List[MetricChange]) -> str:
        """Determine overall assessment from individual changes."""
        # Metrics where increase = regression
        regression_on_increase = {"error_rate", "avg_latency_ms", "p95_latency_ms", "p99_latency_ms", "avg_token_usage"}
        # Metrics where increase = improvement
        improvement_on_increase = {"success_rate"}

        regressions = 0
        improvements = 0

        for change in changes:
            if change.significance == "negligible":
                continue

            weight = {"major": 3, "moderate": 2, "minor": 1}.get(change.significance, 0)

            if change.metric_name in regression_on_increase:
                if change.direction == "increase":
                    regressions += weight
                elif change.direction == "decrease":
                    improvements += weight
            elif change.metric_name in improvement_on_increase:
                if change.direction == "increase":
                    improvements += weight
                elif change.direction == "decrease":
                    regressions += weight

        if regressions == 0 and improvements == 0:
            return "neutral"
        elif regressions > 0 and improvements > 0:
            if regressions > improvements * 2:
                return "regression"
            elif improvements > regressions * 2:
                return "improvement"
            return "mixed"
        elif regressions > 0:
            return "regression"
        else:
            return "improvement"

    def _generate_recommendation(
        self, assessment: str, significant_changes: List[MetricChange]
    ) -> str:
        """Generate a human-readable recommendation."""
        if assessment == "neutral":
            return "No significant changes detected. Safe to proceed."
        elif assessment == "improvement":
            names = [c.metric_name for c in significant_changes[:3]]
            return f"Improvements detected in {', '.join(names)}. Recommended for promotion."
        elif assessment == "regression":
            names = [c.metric_name for c in significant_changes[:3]]
            return f"Regressions detected in {', '.join(names)}. Review before promotion."
        else:
            return "Mixed results detected. Manual review recommended."
