"""
State Engine - Antizipations-Engine und Phasen-Service.

Komponenten:
- AnticipationEngine: Evaluiert Regeln und generiert Vorschläge
- PhaseService: Verwaltet Phasen-Übergänge
"""

import logging
from datetime import datetime
from typing import List, Dict, Any, Optional, Callable
from dataclasses import dataclass

from .models import (
    SchoolYearPhase,
    TeacherContext,
    PhaseInfo,
    get_phase_info,
    PHASE_INFO
)
from .rules import Rule, Suggestion, SuggestionPriority, RULES

logger = logging.getLogger(__name__)


# ============================================================================
# Phasen-Übergänge
# ============================================================================

@dataclass
class PhaseTransition:
    """Definition eines erlaubten Phasen-Übergangs."""
    from_phase: SchoolYearPhase
    to_phase: SchoolYearPhase
    condition: Callable[[TeacherContext], bool]
    auto_trigger: bool = False  # Automatisch wenn Condition erfüllt


# Vordefinierte Übergänge
VALID_TRANSITIONS: List[PhaseTransition] = [
    # Onboarding → SchoolYearStart
    PhaseTransition(
        from_phase=SchoolYearPhase.ONBOARDING,
        to_phase=SchoolYearPhase.SCHOOL_YEAR_START,
        condition=lambda ctx: (
            ctx.has_completed_milestone("school_select") and
            ctx.has_completed_milestone("consent_accept") and
            ctx.has_completed_milestone("profile_complete")
        ),
        auto_trigger=True,
    ),

    # SchoolYearStart → TeachingSetup
    PhaseTransition(
        from_phase=SchoolYearPhase.SCHOOL_YEAR_START,
        to_phase=SchoolYearPhase.TEACHING_SETUP,
        condition=lambda ctx: (
            len(ctx.classes) > 0 and
            ctx.has_completed_milestone("add_students")
        ),
        auto_trigger=True,
    ),

    # TeachingSetup → Performance1
    PhaseTransition(
        from_phase=SchoolYearPhase.TEACHING_SETUP,
        to_phase=SchoolYearPhase.PERFORMANCE_1,
        condition=lambda ctx: (
            ctx.weeks_since_start >= 6 and
            ctx.has_learning_units()
        ),
        auto_trigger=False,  # Manueller Übergang
    ),

    # Performance1 → SemesterEnd
    PhaseTransition(
        from_phase=SchoolYearPhase.PERFORMANCE_1,
        to_phase=SchoolYearPhase.SEMESTER_END,
        condition=lambda ctx: (
            (ctx.is_in_month(1) or ctx.is_in_month(2)) and
            ctx.has_completed_milestone("enter_grades")
        ),
        auto_trigger=True,
    ),

    # SemesterEnd → Teaching2
    PhaseTransition(
        from_phase=SchoolYearPhase.SEMESTER_END,
        to_phase=SchoolYearPhase.TEACHING_2,
        condition=lambda ctx: (
            ctx.has_completed_milestone("generate_certificates")
        ),
        auto_trigger=True,
    ),

    # Teaching2 → Performance2
    PhaseTransition(
        from_phase=SchoolYearPhase.TEACHING_2,
        to_phase=SchoolYearPhase.PERFORMANCE_2,
        condition=lambda ctx: (
            ctx.weeks_since_start >= 30 or
            ctx.is_in_month(4) or ctx.is_in_month(5)
        ),
        auto_trigger=False,
    ),

    # Performance2 → YearEnd
    PhaseTransition(
        from_phase=SchoolYearPhase.PERFORMANCE_2,
        to_phase=SchoolYearPhase.YEAR_END,
        condition=lambda ctx: (
            (ctx.is_in_month(6) or ctx.is_in_month(7)) and
            ctx.has_completed_milestone("enter_final_grades")
        ),
        auto_trigger=True,
    ),

    # YearEnd → Archived
    PhaseTransition(
        from_phase=SchoolYearPhase.YEAR_END,
        to_phase=SchoolYearPhase.ARCHIVED,
        condition=lambda ctx: (
            ctx.has_completed_milestone("generate_final_certificates") and
            ctx.has_completed_milestone("archive_year")
        ),
        auto_trigger=True,
    ),
]


class PhaseService:
    """
    Verwaltet Schuljahres-Phasen und deren Übergänge.

    Funktionen:
    - Phasen-Status abrufen
    - Automatische Übergänge prüfen
    - Manuelle Übergänge durchführen
    """

    def __init__(self, transitions: List[PhaseTransition] = None):
        self.transitions = transitions or VALID_TRANSITIONS

    def get_current_phase_info(self, phase: SchoolYearPhase) -> PhaseInfo:
        """Gibt Metadaten zur aktuellen Phase zurück."""
        return get_phase_info(phase)

    def get_all_phases(self) -> List[Dict[str, Any]]:
        """Gibt alle Phasen mit Metadaten zurück."""
        return [
            {
                "phase": info.phase.value,
                "display_name": info.display_name,
                "description": info.description,
                "typical_months": info.typical_months,
            }
            for info in PHASE_INFO.values()
        ]

    def check_and_transition(self, ctx: TeacherContext) -> Optional[SchoolYearPhase]:
        """
        Prüft ob ein automatischer Phasen-Übergang möglich ist.

        Args:
            ctx: Aktueller TeacherContext

        Returns:
            Neue Phase wenn Übergang stattfand, sonst None
        """
        current_phase = ctx.current_phase

        for transition in self.transitions:
            if (transition.from_phase == current_phase and
                transition.auto_trigger and
                transition.condition(ctx)):

                logger.info(
                    f"Auto-transition from {current_phase} to {transition.to_phase} "
                    f"for teacher {ctx.teacher_id}"
                )
                return transition.to_phase

        return None

    def can_transition_to(self, ctx: TeacherContext, target_phase: SchoolYearPhase) -> bool:
        """
        Prüft ob ein Übergang zu einer bestimmten Phase möglich ist.

        Args:
            ctx: Aktueller TeacherContext
            target_phase: Zielphase

        Returns:
            True wenn Übergang erlaubt
        """
        for transition in self.transitions:
            if (transition.from_phase == ctx.current_phase and
                transition.to_phase == target_phase):
                return transition.condition(ctx)
        return False

    def get_next_phase(self, current: SchoolYearPhase) -> Optional[SchoolYearPhase]:
        """Gibt die nächste Phase in der Sequenz zurück."""
        phase_order = [
            SchoolYearPhase.ONBOARDING,
            SchoolYearPhase.SCHOOL_YEAR_START,
            SchoolYearPhase.TEACHING_SETUP,
            SchoolYearPhase.PERFORMANCE_1,
            SchoolYearPhase.SEMESTER_END,
            SchoolYearPhase.TEACHING_2,
            SchoolYearPhase.PERFORMANCE_2,
            SchoolYearPhase.YEAR_END,
            SchoolYearPhase.ARCHIVED,
        ]

        try:
            idx = phase_order.index(current)
            if idx < len(phase_order) - 1:
                return phase_order[idx + 1]
        except ValueError:
            pass

        return None

    def get_progress_percentage(self, ctx: TeacherContext) -> float:
        """
        Berechnet Fortschritt in der aktuellen Phase.

        Returns:
            Prozent (0-100)
        """
        phase_info = get_phase_info(ctx.current_phase)
        required = set(phase_info.required_actions)
        completed = set(ctx.completed_milestones)

        if not required:
            return 100.0

        done = len(required.intersection(completed))
        return (done / len(required)) * 100


class AnticipationEngine:
    """
    Evaluiert Antizipations-Regeln und generiert priorisierte Vorschläge.

    Die Engine:
    - Wendet alle aktiven Regeln auf den TeacherContext an
    - Priorisiert Vorschläge nach Dringlichkeit
    - Limitiert auf max. 5 Vorschläge
    """

    def __init__(self, rules: List[Rule] = None, max_suggestions: int = 5):
        self.rules = rules or RULES
        self.max_suggestions = max_suggestions

    def get_suggestions(self, ctx: TeacherContext) -> List[Suggestion]:
        """
        Evaluiert alle Regeln und gibt priorisierte Vorschläge zurück.

        Args:
            ctx: TeacherContext mit allen relevanten Informationen

        Returns:
            Liste von max. 5 Vorschlägen, sortiert nach Priorität
        """
        suggestions = []

        for rule in self.rules:
            try:
                suggestion = rule.evaluate(ctx)
                if suggestion:
                    suggestions.append(suggestion)
            except Exception as e:
                logger.warning(f"Rule {rule.id} evaluation failed: {e}")

        return self._prioritize(suggestions)

    def _prioritize(self, suggestions: List[Suggestion]) -> List[Suggestion]:
        """
        Sortiert Vorschläge nach Priorität und limitiert.

        Reihenfolge:
        1. URGENT (1)
        2. HIGH (2)
        3. MEDIUM (3)
        4. LOW (4)
        """
        sorted_suggestions = sorted(
            suggestions,
            key=lambda s: s.priority.value
        )
        return sorted_suggestions[:self.max_suggestions]

    def get_top_suggestion(self, ctx: TeacherContext) -> Optional[Suggestion]:
        """
        Gibt den wichtigsten Vorschlag zurück.

        Args:
            ctx: TeacherContext

        Returns:
            Wichtigster Vorschlag oder None
        """
        suggestions = self.get_suggestions(ctx)
        return suggestions[0] if suggestions else None

    def get_suggestions_by_category(
        self,
        ctx: TeacherContext
    ) -> Dict[str, List[Suggestion]]:
        """
        Gruppiert Vorschläge nach Kategorie.

        Returns:
            Dict mit Kategorien als Keys und Listen von Vorschlägen
        """
        suggestions = self.get_suggestions(ctx)
        by_category: Dict[str, List[Suggestion]] = {}

        for suggestion in suggestions:
            if suggestion.category not in by_category:
                by_category[suggestion.category] = []
            by_category[suggestion.category].append(suggestion)

        return by_category

    def count_by_priority(self, ctx: TeacherContext) -> Dict[str, int]:
        """
        Zählt Vorschläge nach Priorität.

        Returns:
            Dict mit Prioritäten und Anzahlen
        """
        suggestions = self.get_suggestions(ctx)
        counts = {
            "urgent": 0,
            "high": 0,
            "medium": 0,
            "low": 0,
        }

        for s in suggestions:
            if s.priority == SuggestionPriority.URGENT:
                counts["urgent"] += 1
            elif s.priority == SuggestionPriority.HIGH:
                counts["high"] += 1
            elif s.priority == SuggestionPriority.MEDIUM:
                counts["medium"] += 1
            else:
                counts["low"] += 1

        return counts


# ============================================================================
# Factory Functions
# ============================================================================

def create_anticipation_engine(custom_rules: List[Rule] = None) -> AnticipationEngine:
    """Erstellt eine neue AnticipationEngine."""
    return AnticipationEngine(rules=custom_rules)


def create_phase_service(custom_transitions: List[PhaseTransition] = None) -> PhaseService:
    """Erstellt einen neuen PhaseService."""
    return PhaseService(transitions=custom_transitions)
