# ==============================================
# Breakpilot Drive - Learning Rules
# ==============================================
# Adaptive Regeln fuer Lernniveau-Anpassung.
# Integriert mit der bestehenden State Engine.

import logging
from dataclasses import dataclass
from typing import Optional, List, Dict, Any, Callable
from enum import Enum, auto

logger = logging.getLogger(__name__)


class RulePriority(Enum):
    """Priority levels for rule suggestions."""
    LOW = auto()
    MEDIUM = auto()
    HIGH = auto()
    CRITICAL = auto()


class ActionType(str, Enum):
    """Available actions for learning adjustments."""
    INCREASE_DIFFICULTY = "increase_difficulty"
    DECREASE_DIFFICULTY = "decrease_difficulty"
    FOCUS_SUBJECT = "focus_subject"
    ENCOURAGE = "encourage"
    SUGGEST_BREAK = "suggest_break"
    CELEBRATE = "celebrate"
    REVIEW_TOPIC = "review_topic"


@dataclass
class LearningContext:
    """Context for rule evaluation."""
    student_id: str
    overall_level: int
    math_level: float
    german_level: float
    english_level: float
    recent_accuracy: float
    recent_questions: int
    total_play_time_minutes: int
    total_sessions: int
    current_streak: int
    session_duration_minutes: int
    weakest_subject: Optional[str] = None
    strongest_subject: Optional[str] = None

    @classmethod
    def from_learning_state(cls, state: Any, session_stats: Dict[str, Any] = None):
        """Create context from StudentLearningState."""
        session_stats = session_stats or {}

        # Determine weakest and strongest subjects
        levels = {
            "math": state.math_level,
            "german": state.german_level,
            "english": state.english_level,
        }
        weakest = min(levels, key=levels.get)
        strongest = max(levels, key=levels.get)

        return cls(
            student_id=state.student_id,
            overall_level=state.overall_level,
            math_level=state.math_level,
            german_level=state.german_level,
            english_level=state.english_level,
            recent_accuracy=session_stats.get("accuracy", state.accuracy),
            recent_questions=session_stats.get("questions", state.questions_answered),
            total_play_time_minutes=state.total_play_time_minutes,
            total_sessions=state.total_sessions,
            current_streak=session_stats.get("streak", 0),
            session_duration_minutes=session_stats.get("duration_minutes", 0),
            weakest_subject=weakest if levels[weakest] < state.overall_level - 0.5 else None,
            strongest_subject=strongest if levels[strongest] > state.overall_level + 0.5 else None,
        )


@dataclass
class Suggestion:
    """A suggestion generated by a rule."""
    title: str
    description: str
    action: ActionType
    priority: RulePriority
    metadata: Optional[Dict[str, Any]] = None


@dataclass
class LearningRule:
    """A rule for adaptive learning adjustments."""
    id: str
    name: str
    description: str
    condition: Callable[[LearningContext], bool]
    suggestion_generator: Callable[[LearningContext], Suggestion]
    cooldown_minutes: int = 0  # Minimum time between triggers


# ==============================================
# Learning Rules Definitions
# ==============================================

LEARNING_RULES: List[LearningRule] = [
    # ------------------------------------------
    # Difficulty Adjustment Rules
    # ------------------------------------------
    LearningRule(
        id="level_up_ready",
        name="Bereit fuer naechstes Level",
        description="Erhoehe Schwierigkeit wenn 80%+ richtig ueber 10 Fragen",
        condition=lambda ctx: (
            ctx.recent_accuracy >= 0.8 and
            ctx.recent_questions >= 10 and
            ctx.overall_level < 5
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title="Super gemacht!",
            description=f"Du hast {int(ctx.recent_accuracy * 100)}% richtig! Zeit fuer schwerere Aufgaben!",
            action=ActionType.INCREASE_DIFFICULTY,
            priority=RulePriority.HIGH,
            metadata={"new_level": ctx.overall_level + 1}
        ),
        cooldown_minutes=10
    ),

    LearningRule(
        id="level_down_needed",
        name="Schwierigkeit reduzieren",
        description="Verringere Schwierigkeit wenn weniger als 40% richtig",
        condition=lambda ctx: (
            ctx.recent_accuracy < 0.4 and
            ctx.recent_questions >= 5 and
            ctx.overall_level > 1
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title="Lass uns einfacher anfangen",
            description="Kein Problem! Uebung macht den Meister. Wir machen es etwas leichter.",
            action=ActionType.DECREASE_DIFFICULTY,
            priority=RulePriority.HIGH,
            metadata={"new_level": ctx.overall_level - 1}
        ),
        cooldown_minutes=5
    ),

    # ------------------------------------------
    # Subject Focus Rules
    # ------------------------------------------
    LearningRule(
        id="weak_subject_detected",
        name="Schwaches Fach erkannt",
        description="Fokussiere auf Fach mit niedrigstem Level",
        condition=lambda ctx: (
            ctx.weakest_subject is not None and
            ctx.recent_questions >= 15
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title=f"Uebe mehr {_subject_name(ctx.weakest_subject)}",
            description=f"In {_subject_name(ctx.weakest_subject)} kannst du noch besser werden!",
            action=ActionType.FOCUS_SUBJECT,
            priority=RulePriority.MEDIUM,
            metadata={"subject": ctx.weakest_subject}
        ),
        cooldown_minutes=30
    ),

    LearningRule(
        id="strong_subject_celebration",
        name="Starkes Fach feiern",
        description="Lobe wenn ein Fach besonders stark ist",
        condition=lambda ctx: (
            ctx.strongest_subject is not None and
            getattr(ctx, f"{ctx.strongest_subject}_level", 0) >= 4.5
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title=f"Du bist super in {_subject_name(ctx.strongest_subject)}!",
            description="Weiter so! Du bist ein echtes Talent!",
            action=ActionType.CELEBRATE,
            priority=RulePriority.MEDIUM,
            metadata={"subject": ctx.strongest_subject}
        ),
        cooldown_minutes=60
    ),

    # ------------------------------------------
    # Motivation Rules
    # ------------------------------------------
    LearningRule(
        id="streak_celebration",
        name="Serie feiern",
        description="Feiere Erfolgsserien",
        condition=lambda ctx: ctx.current_streak >= 5,
        suggestion_generator=lambda ctx: Suggestion(
            title=f"{ctx.current_streak}x richtig hintereinander!",
            description="Unglaublich! Du bist auf Feuer!",
            action=ActionType.CELEBRATE,
            priority=RulePriority.HIGH,
            metadata={"streak": ctx.current_streak}
        ),
        cooldown_minutes=0  # Can trigger every time
    ),

    LearningRule(
        id="encourage_after_wrong",
        name="Ermutigen nach Fehler",
        description="Ermutige nach mehreren falschen Antworten",
        condition=lambda ctx: (
            ctx.recent_questions >= 3 and
            ctx.recent_accuracy < 0.35 and
            ctx.recent_accuracy > 0  # At least some attempts
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title="Nicht aufgeben!",
            description="Fehler sind zum Lernen da. Du schaffst das!",
            action=ActionType.ENCOURAGE,
            priority=RulePriority.MEDIUM,
            metadata={}
        ),
        cooldown_minutes=2
    ),

    LearningRule(
        id="first_session_welcome",
        name="Erste Session Willkommen",
        description="Begruesse neue Spieler",
        condition=lambda ctx: ctx.total_sessions == 0,
        suggestion_generator=lambda ctx: Suggestion(
            title="Willkommen bei Breakpilot Drive!",
            description="Los geht's! Sammle Punkte und lerne dabei!",
            action=ActionType.ENCOURAGE,
            priority=RulePriority.HIGH,
            metadata={"is_first_session": True}
        ),
        cooldown_minutes=0
    ),

    # ------------------------------------------
    # Break Suggestion Rules
    # ------------------------------------------
    LearningRule(
        id="suggest_break_long_session",
        name="Pause vorschlagen (lange Session)",
        description="Schlage Pause nach 30 Minuten vor",
        condition=lambda ctx: ctx.session_duration_minutes >= 30,
        suggestion_generator=lambda ctx: Suggestion(
            title="Zeit fuer eine Pause?",
            description="Du spielst schon lange. Eine kurze Pause tut gut!",
            action=ActionType.SUGGEST_BREAK,
            priority=RulePriority.LOW,
            metadata={"minutes_played": ctx.session_duration_minutes}
        ),
        cooldown_minutes=30
    ),

    LearningRule(
        id="suggest_break_declining_performance",
        name="Pause vorschlagen (sinkende Leistung)",
        description="Schlage Pause vor wenn Leistung nachlässt",
        condition=lambda ctx: (
            ctx.session_duration_minutes >= 15 and
            ctx.recent_accuracy < 0.5 and
            ctx.recent_questions >= 10
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title="Kurze Pause?",
            description="Eine kleine Pause kann helfen, wieder fit zu werden!",
            action=ActionType.SUGGEST_BREAK,
            priority=RulePriority.MEDIUM,
            metadata={}
        ),
        cooldown_minutes=15
    ),

    # ------------------------------------------
    # Review Topic Rules
    # ------------------------------------------
    LearningRule(
        id="review_failed_topic",
        name="Thema wiederholen",
        description="Schlage Wiederholung vor bei wiederholten Fehlern",
        condition=lambda ctx: (
            ctx.recent_accuracy < 0.3 and
            ctx.recent_questions >= 5
        ),
        suggestion_generator=lambda ctx: Suggestion(
            title="Nochmal ueben?",
            description="Lass uns das Thema nochmal gemeinsam anschauen.",
            action=ActionType.REVIEW_TOPIC,
            priority=RulePriority.MEDIUM,
            metadata={}
        ),
        cooldown_minutes=10
    ),
]


def _subject_name(subject: str) -> str:
    """Get German display name for subject."""
    names = {
        "math": "Mathe",
        "german": "Deutsch",
        "english": "Englisch",
        "general": "Allgemeinwissen"
    }
    return names.get(subject, subject)


class LearningRuleEngine:
    """
    Evaluates learning rules against context.

    Tracks cooldowns and returns applicable suggestions.
    """

    def __init__(self):
        self._cooldowns: Dict[str, float] = {}  # rule_id -> last_triggered_timestamp

    def evaluate(
        self,
        context: LearningContext,
        current_time: float = None
    ) -> List[Suggestion]:
        """
        Evaluate all rules and return applicable suggestions.

        Returns suggestions sorted by priority (highest first).
        """
        import time
        current_time = current_time or time.time()

        suggestions = []

        for rule in LEARNING_RULES:
            # Check cooldown
            last_triggered = self._cooldowns.get(rule.id, 0)
            cooldown_seconds = rule.cooldown_minutes * 60

            if current_time - last_triggered < cooldown_seconds:
                continue

            # Evaluate condition
            try:
                if rule.condition(context):
                    suggestion = rule.suggestion_generator(context)
                    suggestions.append(suggestion)
                    self._cooldowns[rule.id] = current_time
            except Exception as e:
                logger.warning(f"Rule {rule.id} evaluation failed: {e}")

        # Sort by priority (highest first)
        priority_order = {
            RulePriority.CRITICAL: 0,
            RulePriority.HIGH: 1,
            RulePriority.MEDIUM: 2,
            RulePriority.LOW: 3,
        }
        suggestions.sort(key=lambda s: priority_order.get(s.priority, 99))

        return suggestions

    def get_top_suggestion(self, context: LearningContext) -> Optional[Suggestion]:
        """Get the highest priority suggestion."""
        suggestions = self.evaluate(context)
        return suggestions[0] if suggestions else None

    def reset_cooldowns(self):
        """Reset all cooldowns (e.g., for new session)."""
        self._cooldowns.clear()


# Global instance
_rule_engine: Optional[LearningRuleEngine] = None


def get_rule_engine() -> LearningRuleEngine:
    """Get the global rule engine instance."""
    global _rule_engine

    if _rule_engine is None:
        _rule_engine = LearningRuleEngine()

    return _rule_engine


# ==============================================
# Helper Functions
# ==============================================

def calculate_level_adjustment(
    recent_accuracy: float,
    recent_questions: int,
    current_level: int
) -> int:
    """
    Calculate recommended level adjustment.

    Returns: -1 (decrease), 0 (keep), 1 (increase)
    """
    if recent_questions < 5:
        return 0  # Not enough data

    if recent_accuracy >= 0.8 and current_level < 5:
        return 1  # Increase

    if recent_accuracy < 0.4 and current_level > 1:
        return -1  # Decrease

    return 0  # Keep


def get_subject_focus_recommendation(
    math_level: float,
    german_level: float,
    english_level: float,
    overall_level: int
) -> Optional[str]:
    """
    Get recommendation for which subject to focus on.

    Returns subject name or None if all balanced.
    """
    levels = {
        "math": math_level,
        "german": german_level,
        "english": english_level,
    }

    # Find subject most below overall level
    min_subject = min(levels, key=levels.get)
    min_level = levels[min_subject]

    # Only recommend if significantly below overall
    if min_level < overall_level - 0.5:
        return min_subject

    return None
