"""Core metric computations for evaluation.

This module provides the fundamental metric calculations used across
the evaluation system, including accuracy, confidence intervals, and
routing-specific metrics like fraction of oracle.
"""

from collections import Counter
from typing import Sequence

import numpy as np

from embergraphrouter.evals.runners.base import AggregateMetrics, ExampleResult


def accuracy(results: Sequence[ExampleResult]) -> AggregateMetrics:
    """Compute accuracy with 95% confidence interval.

    Args:
        results: Sequence of evaluation results.

    Returns:
        AggregateMetrics with mean accuracy and confidence bounds.
    """
    if not results:
        return AggregateMetrics(mean=0.0, std=0.0, ci_lower=0.0, ci_upper=0.0, n=0)

    correct = [1.0 if r.correct else 0.0 for r in results]
    return mean_with_ci(correct)


def mean_with_ci(
    values: Sequence[float],
    confidence: float = 0.95,
    n_bootstrap: int = 1000,
) -> AggregateMetrics:
    """Compute mean with bootstrap confidence interval.

    Args:
        values: Sequence of numeric values.
        confidence: Confidence level (default 0.95 for 95% CI).
        n_bootstrap: Number of bootstrap samples.

    Returns:
        AggregateMetrics with mean, std, and CI bounds.
    """
    if not values:
        return AggregateMetrics(mean=0.0, std=0.0, ci_lower=0.0, ci_upper=0.0, n=0)

    arr = np.array(values)
    n = len(arr)
    mean = float(np.mean(arr))
    std = float(np.std(arr, ddof=1)) if n > 1 else 0.0

    # Bootstrap confidence interval
    ci_lower, ci_upper = bootstrap_ci(arr, confidence=confidence, n_samples=n_bootstrap)

    return AggregateMetrics(
        mean=mean,
        std=std,
        ci_lower=ci_lower,
        ci_upper=ci_upper,
        n=n,
    )


def bootstrap_ci(
    values: np.ndarray,
    confidence: float = 0.95,
    n_samples: int = 1000,
    seed: int | None = 42,
) -> tuple[float, float]:
    """Compute bootstrap confidence interval for the mean.

    Uses the percentile bootstrap method for robustness.

    Args:
        values: Array of values.
        confidence: Confidence level.
        n_samples: Number of bootstrap resamples.
        seed: Random seed for reproducibility.

    Returns:
        Tuple of (lower_bound, upper_bound).
    """
    if len(values) == 0:
        return 0.0, 0.0

    rng = np.random.default_rng(seed)
    n = len(values)

    # Generate bootstrap samples
    bootstrap_means = np.empty(n_samples)
    for i in range(n_samples):
        sample = values[rng.integers(0, n, size=n)]
        bootstrap_means[i] = np.mean(sample)

    # Compute percentile CI
    alpha = 1 - confidence
    lower = float(np.percentile(bootstrap_means, 100 * alpha / 2))
    upper = float(np.percentile(bootstrap_means, 100 * (1 - alpha / 2)))

    return lower, upper


def frac_of_oracle(
    results: Sequence[ExampleResult],
    oracle_model_field: str = "oracle_model",
) -> float:
    """Compute fraction of queries where router matched oracle selection.

    The oracle is the model that would have given the best result for each
    query (typically from counterfactual evaluation).

    Args:
        results: Sequence of evaluation results with oracle metadata.
        oracle_model_field: Metadata field containing oracle model name.

    Returns:
        Fraction in [0, 1] of oracle-matching selections.
    """
    if not results:
        return 0.0

    matches = 0
    total = 0

    for r in results:
        oracle_model = r.metadata.get(oracle_model_field)
        if oracle_model is not None:
            total += 1
            if r.model_used == oracle_model:
                matches += 1

    return matches / total if total > 0 else 0.0


def tier_distribution(results: Sequence[ExampleResult]) -> dict[str, float]:
    """Compute distribution of queries across routing tiers.

    Args:
        results: Sequence of evaluation results with tier information.

    Returns:
        Dictionary mapping tier names to fractions.
    """
    if not results:
        return {}

    tier_counts: Counter[str] = Counter()
    for r in results:
        tier = r.tier or "unknown"
        tier_counts[tier] += 1

    total = len(results)
    return {tier: count / total for tier, count in tier_counts.items()}


def model_distribution(results: Sequence[ExampleResult]) -> dict[str, float]:
    """Compute distribution of queries across models.

    Args:
        results: Sequence of evaluation results.

    Returns:
        Dictionary mapping model names to fractions.
    """
    if not results:
        return {}

    model_counts: Counter[str] = Counter()
    for r in results:
        model_counts[r.model_used] += 1

    total = len(results)
    return {model: count / total for model, count in model_counts.items()}


def cost_savings_vs_baseline(
    results: Sequence[ExampleResult],
    baseline_cost_per_query: float,
) -> float:
    """Compute cost savings compared to baseline.

    Args:
        results: Sequence of evaluation results with cost data.
        baseline_cost_per_query: Cost if all queries used baseline model.

    Returns:
        Fraction of cost saved (positive = cheaper, negative = more expensive).
    """
    if not results or baseline_cost_per_query <= 0:
        return 0.0

    actual_cost = sum(r.cost_usd for r in results)
    baseline_total = baseline_cost_per_query * len(results)

    return 1.0 - (actual_cost / baseline_total)


def quality_at_cost_threshold(
    results: Sequence[ExampleResult],
    cost_threshold: float,
) -> AggregateMetrics | None:
    """Compute quality metrics for queries under cost threshold.

    Useful for analyzing "what quality can we achieve at budget X?"

    Args:
        results: Sequence of evaluation results.
        cost_threshold: Maximum cost per query to include.

    Returns:
        AggregateMetrics for accuracy of qualifying queries, or None if empty.
    """
    qualifying = [r for r in results if r.cost_usd <= cost_threshold]
    if not qualifying:
        return None
    return accuracy(qualifying)


__all__ = [
    "accuracy",
    "mean_with_ci",
    "bootstrap_ci",
    "frac_of_oracle",
    "tier_distribution",
    "model_distribution",
    "cost_savings_vs_baseline",
    "quality_at_cost_threshold",
]
