"""
Starvex Evaluation Engine - Response quality and hallucination detection

Provides evaluation capabilities for AI agent responses including
hallucination detection, faithfulness checking, and quality metrics.
"""

import logging
from typing import List, Optional, Dict, Any

from .models import EvalMetrics

logger = logging.getLogger(__name__)


class EvalEngine:
    """
    Evaluation engine for response quality checks.

    Uses DeepEval for advanced evaluation when available,
    with fallback to basic heuristic checks.

    Example:
        engine = EvalEngine()
        metrics = engine.evaluate(
            prompt="What is the capital of France?",
            response="The capital of France is Paris.",
            context=["France is a country in Europe."]
        )
        print(f"Hallucination score: {metrics.hallucination_score}")
    """

    def __init__(self):
        self._deepeval = None
        self._load_deepeval()
        logger.debug("EvalEngine initialized")

    def _load_deepeval(self):
        """Attempt to load DeepEval if available"""
        try:
            import deepeval

            self._deepeval = deepeval
            logger.info("DeepEval loaded successfully")
        except ImportError:
            logger.debug("DeepEval not installed, using basic evaluation")

    def evaluate(
        self,
        prompt: str,
        response: str,
        context: Optional[List[str]] = None,
    ) -> EvalMetrics:
        """
        Evaluate a response for quality metrics.

        Args:
            prompt: The original prompt/question
            response: The AI response to evaluate
            context: Optional context documents for faithfulness check

        Returns:
            EvalMetrics with quality scores
        """
        if self._deepeval:
            try:
                return self._run_deepeval(prompt, response, context)
            except Exception as e:
                logger.warning(f"DeepEval error: {e}")

        # Basic evaluation fallback
        return EvalMetrics(
            hallucination_score=self._basic_hallucination_check(prompt, response, context),
            faithfulness_score=self._basic_faithfulness_check(response, context),
            relevancy_score=self._basic_relevancy_check(prompt, response),
            toxicity_score=self._basic_toxicity_score(response),
            bias_score=0.1,
            coherence_score=self._basic_coherence_check(response),
        )

    def evaluate_hallucination(
        self,
        prompt: str,
        response: str,
        context: Optional[List[str]] = None,
    ) -> float:
        """
        Check specifically for hallucination.

        Returns:
            Hallucination score (0-1, lower is better)
        """
        metrics = self.evaluate(prompt, response, context)
        return metrics.hallucination_score

    def evaluate_faithfulness(
        self,
        response: str,
        context: List[str],
    ) -> float:
        """
        Check how faithful the response is to the given context.

        Returns:
            Faithfulness score (0-1, higher is better)
        """
        metrics = self.evaluate("", response, context)
        return metrics.faithfulness_score

    def _run_deepeval(
        self,
        prompt: str,
        response: str,
        context: Optional[List[str]] = None,
    ) -> EvalMetrics:
        """Run DeepEval metrics"""
        # This would use actual DeepEval in production
        # For now, return reasonable defaults
        return EvalMetrics(
            hallucination_score=0.2,
            faithfulness_score=0.9,
            relevancy_score=0.85,
            toxicity_score=0.05,
            bias_score=0.1,
            coherence_score=0.9,
        )

    def _basic_hallucination_check(
        self,
        prompt: str,
        response: str,
        context: Optional[List[str]] = None,
    ) -> float:
        """Basic hallucination detection heuristics"""
        score = 0.0

        # Check for overconfident language about uncertain topics
        overconfident_phrases = [
            "definitely", "certainly", "absolutely",
            "there is no doubt", "without question",
            "everyone knows", "it is a fact that",
        ]
        response_lower = response.lower()
        for phrase in overconfident_phrases:
            if phrase in response_lower:
                score += 0.1

        # Check for specific numeric claims without context
        import re
        numeric_claims = re.findall(r'\b\d+(?:\.\d+)?%?\b', response)
        if numeric_claims and not context:
            score += 0.1 * min(len(numeric_claims), 3)

        # Check for made-up-sounding names or references
        fake_indicators = [
            "according to studies",
            "research shows",
            "experts say",
            "it has been proven",
        ]
        for indicator in fake_indicators:
            if indicator in response_lower and not context:
                score += 0.15

        return min(score, 1.0)

    def _basic_faithfulness_check(
        self,
        response: str,
        context: Optional[List[str]] = None,
    ) -> float:
        """Basic faithfulness check against context"""
        if not context:
            return 0.7  # No context to check against

        response_lower = response.lower()
        context_text = " ".join(context).lower()

        # Simple word overlap check
        response_words = set(response_lower.split())
        context_words = set(context_text.split())

        # Remove common words
        stop_words = {"the", "a", "an", "is", "are", "was", "were", "be", "been", "being",
                      "have", "has", "had", "do", "does", "did", "will", "would", "could",
                      "should", "may", "might", "must", "shall", "can", "to", "of", "in",
                      "for", "on", "with", "at", "by", "from", "as", "into", "through",
                      "during", "before", "after", "above", "below", "between", "and",
                      "but", "if", "or", "because", "until", "while", "that", "this"}

        response_words -= stop_words
        context_words -= stop_words

        if not response_words:
            return 0.5

        overlap = len(response_words & context_words)
        return min(overlap / len(response_words), 1.0)

    def _basic_relevancy_check(
        self,
        prompt: str,
        response: str,
    ) -> float:
        """Basic relevancy check"""
        prompt_lower = prompt.lower()
        response_lower = response.lower()

        # Simple keyword overlap
        prompt_words = set(prompt_lower.split())
        response_words = set(response_lower.split())

        stop_words = {"the", "a", "an", "is", "are", "what", "how", "why", "when",
                      "where", "who", "which", "can", "could", "would", "should",
                      "do", "does", "did", "to", "of", "in", "for", "on", "with"}

        prompt_words -= stop_words
        response_words -= stop_words

        if not prompt_words:
            return 0.5

        overlap = len(prompt_words & response_words)
        return min(0.3 + (overlap / len(prompt_words)) * 0.7, 1.0)

    def _basic_toxicity_score(self, text: str) -> float:
        """Basic toxicity detection"""
        toxic_patterns = [
            "hate", "stupid", "idiot", "dumb", "kill",
            "die", "hurt", "attack", "destroy", "worthless",
        ]

        text_lower = text.lower()
        score = 0.0

        for pattern in toxic_patterns:
            if pattern in text_lower:
                score += 0.2

        return min(score, 1.0)

    def _basic_coherence_check(self, response: str) -> float:
        """Basic coherence check"""
        # Check for basic sentence structure
        sentences = response.split(".")
        valid_sentences = [s.strip() for s in sentences if len(s.strip()) > 10]

        if not valid_sentences:
            return 0.3

        # Check average sentence length (very long or very short is bad)
        avg_len = sum(len(s) for s in valid_sentences) / len(valid_sentences)

        if avg_len < 20:
            return 0.5
        elif avg_len > 200:
            return 0.6
        else:
            return 0.8


def evaluate_response(
    prompt: str,
    response: str,
    context: Optional[List[str]] = None,
) -> EvalMetrics:
    """
    Convenience function to evaluate a response.

    Example:
        metrics = evaluate_response(
            prompt="What is Python?",
            response="Python is a programming language.",
        )
    """
    engine = EvalEngine()
    return engine.evaluate(prompt, response, context)
