"""
Evaluation Engine — Phase 23.7

Computes an effectiveness score for an Experience based on its
accumulated Outcomes. The score determines whether the experience
should be reinforced, downweighted, or deprecated.

Score formula (v1):
    score = success_rate * 0.6
          + (1 - error_rate) * 0.2
          + feedback_score  * 0.2

Status mapping:
    score >= 0.75  → "excellent"
    score >= 0.55  → "good"
    score >= 0.35  → "fair"
    score <  0.35  → "poor"

The Evaluator is stateless — it reads outcomes from the store each call.
"""

import logging
from dataclasses import dataclass, field
from typing import List

from openmemo.improvement.outcome import Outcome

logger = logging.getLogger("openmemo.improvement.evaluator")


@dataclass
class EvaluationResult:
    """Result of evaluating an Experience."""
    experience_id: str
    score: float
    status: str
    total_outcomes: int
    success_rate: float
    error_rate: float
    feedback_score: float
    avg_latency: float
    recommendation: str

    def to_dict(self) -> dict:
        return {
            "experience_id": self.experience_id,
            "score": round(self.score, 4),
            "status": self.status,
            "total_outcomes": self.total_outcomes,
            "success_rate": round(self.success_rate, 4),
            "error_rate": round(self.error_rate, 4),
            "feedback_score": round(self.feedback_score, 4),
            "avg_latency": round(self.avg_latency, 3) if self.avg_latency >= 0 else -1,
            "recommendation": self.recommendation,
        }


STATUS_THRESHOLDS = [
    (0.75, "excellent"),
    (0.55, "good"),
    (0.35, "fair"),
    (0.0,  "poor"),
]


class EvaluationEngine:
    """
    Evaluates an Experience's effectiveness from its Outcome history.

    Args:
        store — SQLiteStore instance
    """

    def __init__(self, store):
        self._store = store

    def evaluate(self, experience_id: str,
                 limit: int = 100) -> EvaluationResult:
        """
        Compute evaluation result for an experience.

        Reads all outcomes for the experience_id, computes metrics,
        and returns an EvaluationResult.
        """
        rows = self._store.list_outcomes(experience_id=experience_id,
                                         limit=limit)
        outcomes = [Outcome.from_dict(r) for r in rows]
        return self._compute(experience_id, outcomes)

    def evaluate_from_outcomes(self, experience_id: str,
                               outcomes: List[Outcome]) -> EvaluationResult:
        """Evaluate from a pre-loaded list of outcomes."""
        return self._compute(experience_id, outcomes)

    # ─── Internal ──────────────────────────────────────────────────

    def _compute(self, experience_id: str,
                 outcomes: List[Outcome]) -> EvaluationResult:
        n = len(outcomes)
        if n == 0:
            return EvaluationResult(
                experience_id=experience_id,
                score=0.0,
                status="no_data",
                total_outcomes=0,
                success_rate=0.0,
                error_rate=0.0,
                feedback_score=0.0,
                avg_latency=-1,
                recommendation="need_more_data",
            )

        successes = sum(1 for o in outcomes if o.is_success)
        failures = sum(1 for o in outcomes if o.is_failure)
        errors = sum(1 for o in outcomes if o.has_error)
        explicit_scores = [o.score for o in outcomes if o.explicit_score is not None]
        latencies = [o.latency for o in outcomes if o.latency is not None and o.latency > 0]

        success_rate = successes / n
        error_rate = errors / n

        # Feedback score: average explicit scores, else infer from success_rate
        if explicit_scores:
            feedback_score = sum(explicit_scores) / len(explicit_scores)
        else:
            feedback_score = success_rate

        # Apply formula
        score = (
            success_rate * 0.6
            + (1 - error_rate) * 0.2
            + feedback_score * 0.2
        )
        score = max(0.0, min(1.0, score))
        avg_latency = sum(latencies) / len(latencies) if latencies else -1

        status = self._status_from_score(score)
        recommendation = self._recommendation(status, n, success_rate)

        logger.debug(
            "[eval] exp=%s n=%d success_rate=%.2f score=%.3f status=%s",
            experience_id, n, success_rate, score, status,
        )
        return EvaluationResult(
            experience_id=experience_id,
            score=score,
            status=status,
            total_outcomes=n,
            success_rate=success_rate,
            error_rate=error_rate,
            feedback_score=feedback_score,
            avg_latency=avg_latency,
            recommendation=recommendation,
        )

    def _status_from_score(self, score: float) -> str:
        for threshold, label in STATUS_THRESHOLDS:
            if score >= threshold:
                return label
        return "poor"

    def _recommendation(self, status: str, n: int, success_rate: float) -> str:
        if status == "no_data" or n < 3:
            return "need_more_data"
        if status == "excellent":
            return "reinforce"
        if status == "good":
            return "keep"
        if status == "fair":
            return "monitor"
        if success_rate < 0.2:
            return "deprecate"
        return "downweight"
