"""
Performance Gap Analyzer

Analyzes logged interactions to identify performance gaps and
areas where SAGE needs improvement.
"""

import logging
from collections import Counter, defaultdict
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

from sage.data_collection.schemas import (
    ExpertFeedback,
    FeedbackRating,
    InteractionOutcome,
    LoggedInteraction,
)

logger = logging.getLogger(__name__)


@dataclass
class TopicGap:
    """Identified gap in a specific topic area."""
    topic: str
    failure_count: int
    total_count: int
    failure_rate: float
    example_queries: list[str] = field(default_factory=list)
    common_errors: list[str] = field(default_factory=list)


@dataclass
class FailurePattern:
    """Pattern of failures across interactions."""
    pattern_type: str  # "low_confidence", "abstention", "error", "factual_error"
    description: str
    occurrence_count: int
    example_interaction_ids: list[str] = field(default_factory=list)
    suggested_fix: str = ""


@dataclass
class GapReport:
    """Complete performance gap analysis report."""

    # Summary metrics
    total_interactions: int = 0
    success_rate: float = 0.0
    abstention_rate: float = 0.0
    error_rate: float = 0.0
    average_confidence: float = 0.0

    # Expert feedback summary
    total_feedback: int = 0
    average_accuracy_score: float = 0.0
    factual_error_rate: float = 0.0
    false_positive_rate: float = 0.0
    false_negative_rate: float = 0.0

    # Identified gaps
    topic_gaps: list[TopicGap] = field(default_factory=list)
    failure_patterns: list[FailurePattern] = field(default_factory=list)
    weak_query_types: list[str] = field(default_factory=list)

    # Recommendations
    training_focus_areas: list[str] = field(default_factory=list)
    prompt_engineering_suggestions: list[str] = field(default_factory=list)
    data_collection_priorities: list[str] = field(default_factory=list)

    # Metadata
    analysis_timestamp: datetime = field(default_factory=datetime.now)
    analysis_period_start: datetime | None = None
    analysis_period_end: datetime | None = None

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary for serialization."""
        return {
            "summary": {
                "total_interactions": self.total_interactions,
                "success_rate": self.success_rate,
                "abstention_rate": self.abstention_rate,
                "error_rate": self.error_rate,
                "average_confidence": self.average_confidence,
            },
            "expert_feedback": {
                "total_feedback": self.total_feedback,
                "average_accuracy_score": self.average_accuracy_score,
                "factual_error_rate": self.factual_error_rate,
                "false_positive_rate": self.false_positive_rate,
                "false_negative_rate": self.false_negative_rate,
            },
            "topic_gaps": [
                {
                    "topic": g.topic,
                    "failure_count": g.failure_count,
                    "total_count": g.total_count,
                    "failure_rate": g.failure_rate,
                    "example_queries": g.example_queries[:3],
                    "common_errors": g.common_errors,
                }
                for g in self.topic_gaps
            ],
            "failure_patterns": [
                {
                    "pattern_type": p.pattern_type,
                    "description": p.description,
                    "occurrence_count": p.occurrence_count,
                    "suggested_fix": p.suggested_fix,
                }
                for p in self.failure_patterns
            ],
            "weak_query_types": self.weak_query_types,
            "recommendations": {
                "training_focus_areas": self.training_focus_areas,
                "prompt_engineering_suggestions": self.prompt_engineering_suggestions,
                "data_collection_priorities": self.data_collection_priorities,
            },
            "metadata": {
                "analysis_timestamp": self.analysis_timestamp.isoformat(),
                "period_start": self.analysis_period_start.isoformat() if self.analysis_period_start else None,
                "period_end": self.analysis_period_end.isoformat() if self.analysis_period_end else None,
            },
        }


class PerformanceGapAnalyzer:
    """Analyzes SAGE performance to identify improvement opportunities.

    Uses logged interactions and expert feedback to:
    1. Identify weak topic areas
    2. Find common failure patterns
    3. Recommend training focus areas
    4. Suggest prompt engineering improvements

    Example:
        analyzer = PerformanceGapAnalyzer()

        # Analyze interactions
        report = analyzer.analyze(interactions, feedback_map)

        # Get weak topics
        print(f"Weak topics: {report.weak_query_types}")

        # Get training recommendations
        print(f"Focus areas: {report.training_focus_areas}")
    """

    # Topic keywords for classification
    TOPIC_KEYWORDS = {
        "mass_limits": ["mass", "limit", "kg", "kilogram", "subcritical mass"],
        "geometry": ["geometry", "shape", "sphere", "cylinder", "slab", "array"],
        "moderation": ["moderation", "moderator", "water", "polyethylene", "hydrogen"],
        "reflection": ["reflection", "reflector", "concrete", "water reflected"],
        "enrichment": ["enrichment", "enriched", "u-235", "leu", "heu"],
        "solutions": ["solution", "concentration", "g/l", "uranyl", "plutonium nitrate"],
        "criticality_safety": ["criticality", "k-eff", "keff", "critical", "subcritical"],
        "double_contingency": ["double contingency", "upset", "abnormal", "two independent"],
        "standards": ["ansi", "ans-8", "standard", "regulation", "requirement"],
        "calculations": ["calculate", "compute", "estimate", "determine"],
    }

    def __init__(
        self,
        low_confidence_threshold: float = 0.6,
        min_topic_occurrences: int = 5,
    ):
        """Initialize analyzer.

        Args:
            low_confidence_threshold: Threshold below which confidence is "low".
            min_topic_occurrences: Minimum occurrences to consider a topic.
        """
        self.low_confidence_threshold = low_confidence_threshold
        self.min_topic_occurrences = min_topic_occurrences

    def analyze(
        self,
        interactions: list[LoggedInteraction],
        feedback_map: dict[str, ExpertFeedback] | None = None,
    ) -> GapReport:
        """Analyze interactions to identify performance gaps.

        Args:
            interactions: List of logged interactions.
            feedback_map: Map of interaction_id -> expert feedback.

        Returns:
            GapReport with identified gaps and recommendations.
        """
        feedback_map = feedback_map or {}

        report = GapReport(
            total_interactions=len(interactions),
            total_feedback=len(feedback_map),
        )

        if not interactions:
            return report

        # Set analysis period
        timestamps = [i.timestamp for i in interactions]
        report.analysis_period_start = min(timestamps)
        report.analysis_period_end = max(timestamps)

        # Calculate summary metrics
        self._calculate_summary_metrics(interactions, report)

        # Analyze expert feedback
        if feedback_map:
            self._analyze_feedback(feedback_map, report)

        # Identify topic gaps
        self._identify_topic_gaps(interactions, feedback_map, report)

        # Find failure patterns
        self._find_failure_patterns(interactions, feedback_map, report)

        # Identify weak query types
        self._identify_weak_query_types(interactions, report)

        # Generate recommendations
        self._generate_recommendations(report)

        logger.info(
            f"Gap analysis complete: {len(report.topic_gaps)} topic gaps, "
            f"{len(report.failure_patterns)} patterns identified"
        )

        return report

    def _calculate_summary_metrics(
        self,
        interactions: list[LoggedInteraction],
        report: GapReport,
    ) -> None:
        """Calculate summary metrics from interactions."""
        outcomes = Counter(i.outcome for i in interactions)
        total = len(interactions)

        report.success_rate = outcomes.get(InteractionOutcome.SUCCESS, 0) / total
        report.abstention_rate = outcomes.get(InteractionOutcome.ABSTAINED, 0) / total
        report.error_rate = outcomes.get(InteractionOutcome.ERROR, 0) / total

        confidences = [i.response_confidence for i in interactions if i.response_confidence > 0]
        report.average_confidence = sum(confidences) / len(confidences) if confidences else 0.0

    def _analyze_feedback(
        self,
        feedback_map: dict[str, ExpertFeedback],
        report: GapReport,
    ) -> None:
        """Analyze expert feedback for quality signals."""
        feedback_list = list(feedback_map.values())

        if not feedback_list:
            return

        # Calculate average scores
        accuracy_scores = [f.accuracy_score.value for f in feedback_list]
        report.average_accuracy_score = sum(accuracy_scores) / len(accuracy_scores)

        # Calculate error rates
        report.factual_error_rate = sum(
            1 for f in feedback_list if f.contains_factual_error
        ) / len(feedback_list)

        report.false_positive_rate = sum(
            1 for f in feedback_list if f.is_dual_use_false_positive
        ) / len(feedback_list)

        report.false_negative_rate = sum(
            1 for f in feedback_list if f.is_dual_use_false_negative
        ) / len(feedback_list)

    def _identify_topic_gaps(
        self,
        interactions: list[LoggedInteraction],
        feedback_map: dict[str, ExpertFeedback],
        report: GapReport,
    ) -> None:
        """Identify weak topic areas."""
        # Group interactions by topic
        topic_interactions: dict[str, list[LoggedInteraction]] = defaultdict(list)

        for interaction in interactions:
            topics = self._extract_topics(interaction.query)
            for topic in topics:
                topic_interactions[topic].append(interaction)

        # Analyze each topic
        for topic, topic_ints in topic_interactions.items():
            if len(topic_ints) < self.min_topic_occurrences:
                continue

            # Count failures
            failures = [
                i for i in topic_ints
                if (
                    i.outcome == InteractionOutcome.ERROR or
                    i.response_confidence < self.low_confidence_threshold or
                    (i.interaction_id in feedback_map and
                     feedback_map[i.interaction_id].contains_factual_error)
                )
            ]

            failure_rate = len(failures) / len(topic_ints)

            if failure_rate > 0.1:  # More than 10% failure rate
                gap = TopicGap(
                    topic=topic,
                    failure_count=len(failures),
                    total_count=len(topic_ints),
                    failure_rate=failure_rate,
                    example_queries=[i.query for i in failures[:5]],
                    common_errors=self._extract_common_errors(failures, feedback_map),
                )
                report.topic_gaps.append(gap)

        # Sort by failure rate
        report.topic_gaps.sort(key=lambda g: g.failure_rate, reverse=True)

    def _find_failure_patterns(
        self,
        interactions: list[LoggedInteraction],
        feedback_map: dict[str, ExpertFeedback],
        report: GapReport,
    ) -> None:
        """Find common patterns in failures."""
        patterns = []

        # Pattern: Low confidence responses
        low_conf = [
            i for i in interactions
            if i.response_confidence < self.low_confidence_threshold
        ]
        if len(low_conf) > 5:
            patterns.append(FailurePattern(
                pattern_type="low_confidence",
                description="Responses with confidence below threshold",
                occurrence_count=len(low_conf),
                example_interaction_ids=[i.interaction_id for i in low_conf[:5]],
                suggested_fix="Add more training data for low-confidence topics, "
                              "or improve RAG retrieval for these query types.",
            ))

        # Pattern: Abstentions that may be false positives
        false_abstentions = [
            i for i in interactions
            if (
                i.abstention_triggered and
                i.interaction_id in feedback_map and
                feedback_map[i.interaction_id].is_dual_use_false_positive
            )
        ]
        if false_abstentions:
            patterns.append(FailurePattern(
                pattern_type="false_positive_abstention",
                description="Legitimate queries incorrectly blocked",
                occurrence_count=len(false_abstentions),
                example_interaction_ids=[i.interaction_id for i in false_abstentions[:5]],
                suggested_fix="Refine dual-use detection patterns to reduce false positives.",
            ))

        # Pattern: Factual errors
        factual_errors = [
            i for i in interactions
            if (
                i.interaction_id in feedback_map and
                feedback_map[i.interaction_id].contains_factual_error
            )
        ]
        if factual_errors:
            patterns.append(FailurePattern(
                pattern_type="factual_error",
                description="Responses containing factual errors",
                occurrence_count=len(factual_errors),
                example_interaction_ids=[i.interaction_id for i in factual_errors[:5]],
                suggested_fix="Add verified examples for these topics to training data. "
                              "Review RAG sources for accuracy.",
            ))

        # Pattern: Missing tool usage
        should_use_tools = [
            i for i in interactions
            if (
                i.query_type == "calculation" and
                not i.tool_executions
            )
        ]
        if len(should_use_tools) > 3:
            patterns.append(FailurePattern(
                pattern_type="missing_tool_usage",
                description="Calculation queries without tool usage",
                occurrence_count=len(should_use_tools),
                example_interaction_ids=[i.interaction_id for i in should_use_tools[:5]],
                suggested_fix="Improve query routing to detect calculation intents.",
            ))

        report.failure_patterns = patterns

    def _identify_weak_query_types(
        self,
        interactions: list[LoggedInteraction],
        report: GapReport,
    ) -> None:
        """Identify query types with poor performance."""
        # Group by query type
        type_performance: dict[str, dict[str, float]] = defaultdict(
            lambda: {"total": 0, "success": 0}
        )

        for i in interactions:
            type_performance[i.query_type]["total"] += 1
            if i.outcome == InteractionOutcome.SUCCESS and i.response_confidence >= 0.7:
                type_performance[i.query_type]["success"] += 1

        # Find weak types
        weak_types = []
        for qtype, perf in type_performance.items():
            if perf["total"] >= 5:
                success_rate = perf["success"] / perf["total"]
                if success_rate < 0.8:
                    weak_types.append(qtype)

        report.weak_query_types = weak_types

    def _generate_recommendations(self, report: GapReport) -> None:
        """Generate actionable recommendations based on analysis."""
        # Training focus areas
        training_focus = []
        for gap in report.topic_gaps[:5]:  # Top 5 gaps
            training_focus.append(
                f"Collect more training data for '{gap.topic}' queries "
                f"({gap.failure_rate:.0%} failure rate)"
            )

        if report.factual_error_rate > 0.05:
            training_focus.append(
                f"Focus on factual accuracy - {report.factual_error_rate:.1%} of responses "
                "contained errors"
            )

        report.training_focus_areas = training_focus

        # Prompt engineering suggestions
        prompt_suggestions = []
        if report.average_confidence < 0.7:
            prompt_suggestions.append(
                "Consider adding confidence calibration to system prompt"
            )

        if report.abstention_rate > 0.2:
            prompt_suggestions.append(
                "Review abstention thresholds - may be too aggressive"
            )

        if report.false_positive_rate > 0.05:
            prompt_suggestions.append(
                "Refine dual-use detection patterns to reduce false positives"
            )

        report.prompt_engineering_suggestions = prompt_suggestions

        # Data collection priorities
        data_priorities = []
        for qtype in report.weak_query_types:
            data_priorities.append(f"Collect more '{qtype}' query examples")

        for gap in report.topic_gaps[:3]:
            data_priorities.append(
                f"Gather expert-validated examples for '{gap.topic}'"
            )

        report.data_collection_priorities = data_priorities

    def _extract_topics(self, query: str) -> list[str]:
        """Extract topic tags from a query."""
        query_lower = query.lower()
        topics = []

        for topic, keywords in self.TOPIC_KEYWORDS.items():
            if any(kw in query_lower for kw in keywords):
                topics.append(topic)

        return topics if topics else ["general"]

    def _extract_common_errors(
        self,
        failures: list[LoggedInteraction],
        feedback_map: dict[str, ExpertFeedback],
    ) -> list[str]:
        """Extract common error types from failures."""
        errors = []

        for f in failures:
            if f.interaction_id in feedback_map:
                feedback = feedback_map[f.interaction_id]
                if feedback.correction:
                    errors.append(feedback.correction[:100])

        # Return unique errors
        return list(set(errors))[:5]

    def identify_weak_topics(
        self,
        interactions: list[LoggedInteraction],
    ) -> list[str]:
        """Convenience method to identify weak topics.

        Args:
            interactions: List of logged interactions.

        Returns:
            List of weak topic names.
        """
        report = self.analyze(interactions)
        return [g.topic for g in report.topic_gaps]

    def recommend_training_focus(
        self,
        interactions: list[LoggedInteraction],
        feedback_map: dict[str, ExpertFeedback] | None = None,
    ) -> list[str]:
        """Get training focus recommendations.

        Args:
            interactions: List of logged interactions.
            feedback_map: Map of interaction_id -> feedback.

        Returns:
            List of recommended training focus areas.
        """
        report = self.analyze(interactions, feedback_map)
        return report.training_focus_areas
