from dataclasses import dataclass, field
from typing import Dict, List, Tuple, Any
from openevalkit.score import Score
from openevalkit.judgment import Judgment


@dataclass
class EvaluationResult:
    """
    Results from an evaluation run.

    Contains scores, judgments, aggregates, and metadata.

    Examples:
        >>> # Scorers only
        >>> results = evaluate(dataset, scorers=[ExactMatch()])
        >>> print(results.aggregates)
        {'exact_match': 0.85}
        
        >>> # Judges only
        >>> results = evaluate(dataset, judges=[llm_judge])
        >>> print(results.judgments['llm_judge_gpt-4o'][0].score)
        0.92
        
        >>> # Both
        >>> results = evaluate(dataset, scorers=[ExactMatch()], judges=[judge])
        >>> print(results.scores['exact_match'][0].value)
        >>> print(results.judgments['llm_judge_gpt-4o'][0].score)
    """
    scores: Dict[str, List[Score]]
    aggregates: Dict[str, float]
    judgments: Dict[str, List[Judgment]] = field(default_factory=dict)  # ← Add this
    failed_runs: List[Tuple[str, str, Exception]] = field(default_factory=list)  # ← Add scorer/judge name

    # Additional metadata
    metadata: Dict[str, Any] = field(default_factory=dict)

    def summary(self) -> str:
        """
        Generate a summary string of results.
        
        Returns:
            Human-readable summary
        """
        lines = ["Evaluation Results:", "=" * 50]
        
        # Aggregates
        lines.append("\nAggregates:")
        for metric, value in self.aggregates.items():
            lines.append(f"  {metric}: {value:.4f}")
        
        # Summary counts
        lines.append(f"\nScorers: {len(self.scores)}")
        lines.append(f"Judges: {len(self.judgments)}")
        
        # Failed runs
        if self.failed_runs:
            lines.append(f"\nFailed runs: {len(self.failed_runs)}")
            for run_id, evaluator_name, error in self.failed_runs[:5]:
                lines.append(f"  {run_id} ({evaluator_name}): {error}")
        
        return "\n".join(lines)