"""Type definitions for Klira evaluation system."""

from typing import Dict, List, Optional, Any
from dataclasses import dataclass, field
from datetime import datetime


@dataclass
class ComplianceReport:
    """Compliance-focused evaluation report."""

    # Overall metrics
    guardrail_recall: float  # % of violations correctly caught
    guardrail_precision: float  # % of blocks that were justified
    policy_coverage_rate: Optional[float]  # % of policies tested
    avg_confidence: float  # Average confidence across decisions

    # Policy-level breakdown
    policy_metrics: Dict[str, Dict[str, float]]  # policy_id -> {recall, precision, ...}
    untested_policies: List[str]  # Policies with < min_trigger_count

    # Layer analysis
    layer_distribution: Dict[str, Dict[str, Any]]  # layer -> {count, percentage}
    low_confidence_count: int
    llm_fallback_rate: float

    # Fuzzy matching
    fuzzy_match_recall: Optional[float] = None
    fuzzy_caught_count: Optional[int] = None
    fuzzy_missed_count: Optional[int] = None

    # Recommendations
    recommendations: List[str] = field(default_factory=list)


@dataclass
class KliraEvalResult:
    """Evaluation result with trace-based model support.

    **TRACE-BASED EVALUATION MODEL:**
    The SDK has migrated from SDK-side evaluation to a platform-side trace-based
    model. This result object now contains basic metadata and test cases with
    their outputs. Detailed evaluation metrics (pass rate, compliance reports,
    etc.) are generated by the platform from captured traces.

    **What You Get:**
    - test_cases: List of test cases with actual_output populated
    - Trace metadata: total_test_cases, duration_seconds, timestamps
    - Context: experiment_id, organization_id, project_id for tracing

    **What You Don't Get (Deprecated):**
    - pass_rate, passed_test_cases, failed_test_cases: Use platform API
    - avg_scores, metric_results: Use platform API
    - compliance_report: Use platform API

    **How to Access Full Results:**
    1. The SDK captures execution traces automatically via OTLP
    2. Platform evaluates traces asynchronously using LLM judge
    3. Access detailed results via:
       - Klira Dashboard: https://dashboard.getklira.com
       - Platform API: GET /api/v1/eval-runs/{evals_run}
       - Programmatically: See integration examples

    **Migration from Old Model:**
    If you were using DeepEval metrics (old model), the migration is simple:
    - Remove evaluators parameter from evaluate() call
    - Remove upload_to_hub parameter
    - Access results from platform API instead of result object

    See migration guide: https://docs.getklira.com/migration/trace-based-evals
    """

    # Test case data (trace-based model)
    total_test_cases: int  # Total number of test cases executed
    dataset_path: Optional[str] = None  # Path to dataset file
    test_cases: Optional[List[Any]] = None  # List of LLMTestCase with actual_output
    evals_run: Optional[str] = None  # Eval run ID from config
    created_at: Optional[datetime] = None  # Timestamp of evaluation
    duration_seconds: Optional[float] = None  # Total execution time

    # Legacy fields (backward compatibility) - DEPRECATED
    # These fields are kept for backward compatibility but are no longer
    # populated in the new trace-based model
    pass_rate: float = 0.0  # DEPRECATED: No longer computed
    passed_test_cases: int = 0  # DEPRECATED: Use platform API
    failed_test_cases: int = 0  # DEPRECATED: Use platform API
    avg_scores: Dict[str, float] = field(default_factory=dict)  # DEPRECATED
    metric_results: Dict[str, List[Dict[str, Any]]] = field(
        default_factory=dict
    )  # DEPRECATED
    compliance_report: Optional[ComplianceReport] = (
        None  # DEPRECATED: Platform generates
    )

    # Metadata (optional)
    experiment_id: Optional[str] = None
    organization_id: Optional[str] = None
    project_id: Optional[str] = None

    # Hub integration (optional)
    hub_url: Optional[str] = None
    hub_eval_run_id: Optional[str] = None

    # Raw DeepEval results (optional)
    raw_results: Any = None  # DeepEval's EvaluateOutput (deprecated)

    def compliance_summary(self) -> Dict[str, Any]:
        """
        Get quick compliance summary with key metrics.

        Returns:
            Dictionary with essential compliance metrics for quick review

        Example:
            >>> result = evaluate(...)
            >>> summary = result.compliance_summary()
            >>> print(f"Recall: {summary['guardrail_recall']:.1%}")
        """
        if self.compliance_report is None:
            return {}
        return {
            "guardrail_recall": self.compliance_report.guardrail_recall,
            "guardrail_precision": self.compliance_report.guardrail_precision,
            "policy_coverage": self.compliance_report.policy_coverage_rate,
            "avg_confidence": self.compliance_report.avg_confidence,
            "llm_fallback_rate": self.compliance_report.llm_fallback_rate,
            "untested_policies": self.compliance_report.untested_policies,
        }

    def get_failed_test_cases(self) -> List[Dict[str, Any]]:
        """
        Get list of failed test cases with details.

        Returns:
            List of failed test cases with input, output, and error details

        Example:
            >>> result = evaluate(...)
            >>> for failure in result.get_failed_test_cases():
            ...     print(f"Failed: {failure['input']}")
            ...     print(f"Error: {failure['error']}")
        """
        if not self.raw_results:
            return []

        failed_cases = []
        for test_result in getattr(self.raw_results, "test_results", []):
            if not test_result.success:
                failed_cases.append(
                    {
                        "input": test_result.input,
                        "actual_output": test_result.actual_output,
                        "expected_output": test_result.expected_output,
                        "metrics_data": test_result.metrics_data,
                        "success": test_result.success,
                    }
                )

        return failed_cases

    def get_low_confidence_cases(self) -> List[Dict[str, Any]]:
        """
        Get test cases with low confidence scores.

        Returns:
            List of test cases with confidence below threshold

        Example:
            >>> result = evaluate(...)
            >>> for case in result.get_low_confidence_cases():
            ...     print(f"Low confidence ({case['confidence']:.2f}): {case['input']}")
        """
        if not self.raw_results:
            return []

        low_conf_cases = []
        for test_result in getattr(self.raw_results, "test_results", []):
            # Extract guardrail result from test case
            test_case = getattr(test_result, "test_case", None)
            if test_case and hasattr(test_case, "additional_metadata"):
                guardrail_result = test_case.additional_metadata.get(
                    "guardrail_result", {}
                )
                confidence = guardrail_result.get("confidence", 1.0)

                if confidence < 0.7:  # Default threshold
                    low_conf_cases.append(
                        {
                            "input": test_result.input[:100],  # First 100 chars
                            "confidence": confidence,
                            "decision_layer": guardrail_result.get("decision_layer"),
                            "allowed": guardrail_result.get("allowed"),
                        }
                    )

        return low_conf_cases

    def print_summary(self) -> None:
        """
        Print a formatted summary of evaluation results.

        Example:
            >>> result = evaluate(...)
            >>> result.print_summary()
            Evaluation Summary
            ==================
            Pass Rate: 95.0%
            Total Cases: 100
            Passed: 95
            Failed: 5
            ...
        """
        print("\nEvaluation Summary")
        print("=" * 50)
        print(f"Pass Rate: {self.pass_rate:.1%}")
        print(f"Total Cases: {self.total_test_cases}")
        print(f"Passed: {self.passed_test_cases}")
        print(f"Failed: {self.failed_test_cases}")

        if self.duration_seconds:
            print(f"Duration: {self.duration_seconds:.2f}s")

        if self.compliance_report is not None:
            print("\nCompliance Metrics")
            print("-" * 50)
            print(f"Guardrail Recall: {self.compliance_report.guardrail_recall:.1%}")
            print(
                f"Guardrail Precision: {self.compliance_report.guardrail_precision:.1%}"
            )
            if self.compliance_report.policy_coverage_rate is not None:
                print(
                    f"Policy Coverage: {self.compliance_report.policy_coverage_rate:.1%}"
                )
            else:
                print("Policy Coverage: N/A")
            print(f"Avg Confidence: {self.compliance_report.avg_confidence:.2f}")
            print(f"LLM Fallback Rate: {self.compliance_report.llm_fallback_rate:.1%}")

            if self.compliance_report.untested_policies:
                print(
                    f"\nUntested Policies: {', '.join(self.compliance_report.untested_policies)}"
                )

            if self.compliance_report.recommendations:
                print("\nRecommendations")
                print("-" * 50)
                for i, rec in enumerate(self.compliance_report.recommendations, 1):
                    print(f"{i}. {rec}")

        if self.hub_url:
            print(f"\nView full results: {self.hub_url}")
        print()

    def is_passing(self, min_pass_rate: float = 0.90) -> bool:
        """
        Check if evaluation meets minimum pass rate.

        Args:
            min_pass_rate: Minimum required pass rate (default: 0.90)

        Returns:
            True if pass rate meets or exceeds threshold

        Example:
            >>> result = evaluate(...)
            >>> if not result.is_passing(min_pass_rate=0.95):
            ...     raise Exception("Evaluation failed to meet quality threshold")
        """
        return self.pass_rate >= min_pass_rate


@dataclass
class TestCaseResult:
    """Individual test case evaluation result."""

    test_case_id: str
    input: str
    expected_output: Optional[str]
    actual_output: str

    # Guardrails decision
    guardrail_decision: Dict[str, Any]

    # Metric scores
    metric_scores: Dict[str, float]
    passed: bool

    # Metadata
    latency_ms: float
    error: Optional[str] = None
