"""
Tests für State Engine.

Testet:
- Phasen-Management
- Antizipations-Regeln
- API Endpoints
"""

import pytest
from datetime import datetime, timedelta
from fastapi.testclient import TestClient

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from main import app
from state_engine import (
    AnticipationEngine,
    PhaseService,
    TeacherContext,
    SchoolYearPhase,
    ClassSummary,
    Event,
    TeacherStats,
    Suggestion,
    SuggestionPriority,
    RULES
)

client = TestClient(app)


class TestSchoolYearPhase:
    """Tests für SchoolYearPhase."""

    def test_all_phases_defined(self):
        """Testet dass alle 9 Phasen definiert sind."""
        phases = list(SchoolYearPhase)
        assert len(phases) == 9
        assert SchoolYearPhase.ONBOARDING in phases
        assert SchoolYearPhase.ARCHIVED in phases

    def test_phase_values(self):
        """Testet Phase-Werte."""
        assert SchoolYearPhase.ONBOARDING.value == "onboarding"
        assert SchoolYearPhase.SEMESTER_END.value == "semester_end"


class TestTeacherContext:
    """Tests für TeacherContext."""

    def test_create_context(self):
        """Testet Erstellung eines Kontexts."""
        ctx = TeacherContext(
            teacher_id="test-teacher",
            school_id="test-school",
            school_year_id="test-year"
        )

        assert ctx.teacher_id == "test-teacher"
        assert ctx.current_phase == SchoolYearPhase.ONBOARDING

    def test_has_completed_milestone(self):
        """Testet Meilenstein-Prüfung."""
        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            completed_milestones=["consent_accept", "profile_complete"]
        )

        assert ctx.has_completed_milestone("consent_accept")
        assert not ctx.has_completed_milestone("school_select")

    def test_has_learning_units(self):
        """Testet Lerneinheiten-Prüfung."""
        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            stats=TeacherStats(learning_units_created=0)
        )
        assert not ctx.has_learning_units()

        ctx.stats.learning_units_created = 5
        assert ctx.has_learning_units()

    def test_to_dict(self):
        """Testet Konvertierung zu Dict."""
        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test"
        )
        d = ctx.to_dict()

        assert "teacher_id" in d
        assert "current_phase" in d
        assert d["current_phase"] == "onboarding"


class TestPhaseService:
    """Tests für PhaseService."""

    def test_get_next_phase(self):
        """Testet nächste Phase."""
        service = PhaseService()

        assert service.get_next_phase(SchoolYearPhase.ONBOARDING) == SchoolYearPhase.SCHOOL_YEAR_START
        assert service.get_next_phase(SchoolYearPhase.SCHOOL_YEAR_START) == SchoolYearPhase.TEACHING_SETUP
        assert service.get_next_phase(SchoolYearPhase.ARCHIVED) is None

    def test_check_and_transition_onboarding(self):
        """Testet automatischen Übergang von Onboarding."""
        service = PhaseService()

        # Ohne alle Meilensteine
        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.ONBOARDING,
            completed_milestones=["school_select", "consent_accept"]
        )
        assert service.check_and_transition(ctx) is None

        # Mit allen Meilensteinen
        ctx.completed_milestones.append("profile_complete")
        new_phase = service.check_and_transition(ctx)
        assert new_phase == SchoolYearPhase.SCHOOL_YEAR_START

    def test_can_transition_to(self):
        """Testet Übergangs-Prüfung."""
        service = PhaseService()

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.ONBOARDING,
            completed_milestones=["school_select", "consent_accept", "profile_complete"]
        )

        # Erlaubter Übergang
        assert service.can_transition_to(ctx, SchoolYearPhase.SCHOOL_YEAR_START)

        # Nicht erlaubter Übergang (falsche Reihenfolge)
        assert not service.can_transition_to(ctx, SchoolYearPhase.PERFORMANCE_1)


class TestAnticipationEngine:
    """Tests für AnticipationEngine."""

    def test_get_suggestions_no_classes(self):
        """Testet Vorschläge ohne Klassen."""
        engine = AnticipationEngine()

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.SCHOOL_YEAR_START,
            classes=[]
        )

        suggestions = engine.get_suggestions(ctx)

        # Sollte "Erste Klasse anlegen" Vorschlag haben
        assert any(s.id == "create_first_class" for s in suggestions)

    def test_get_suggestions_priority_order(self):
        """Testet Priorisierung der Vorschläge."""
        engine = AnticipationEngine()

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.ONBOARDING,
            completed_milestones=[]
        )

        suggestions = engine.get_suggestions(ctx)

        # Vorschläge sollten nach Priorität sortiert sein
        if len(suggestions) >= 2:
            assert suggestions[0].priority.value <= suggestions[1].priority.value

    def test_get_suggestions_max_limit(self):
        """Testet Limit von max. 5 Vorschlägen."""
        engine = AnticipationEngine(max_suggestions=5)

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.PERFORMANCE_1,
            stats=TeacherStats(
                exams_scheduled=5,
                exams_graded=0,
                unanswered_messages=10
            ),
            upcoming_events=[
                Event(
                    type="exam",
                    title="Test",
                    date=datetime.now() + timedelta(days=3),
                    in_days=3
                )
            ]
        )

        suggestions = engine.get_suggestions(ctx)
        assert len(suggestions) <= 5

    def test_get_top_suggestion(self):
        """Testet Top-Vorschlag."""
        engine = AnticipationEngine()

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.SCHOOL_YEAR_START,
            classes=[]
        )

        top = engine.get_top_suggestion(ctx)
        assert top is not None
        assert top.priority == SuggestionPriority.URGENT

    def test_suggestions_by_category(self):
        """Testet Gruppierung nach Kategorie."""
        engine = AnticipationEngine()

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.ONBOARDING
        )

        by_category = engine.get_suggestions_by_category(ctx)
        assert isinstance(by_category, dict)


class TestRules:
    """Tests für vordefinierte Regeln."""

    def test_rules_count(self):
        """Testet Anzahl der Regeln."""
        assert len(RULES) >= 15  # Mindestens 15 Regeln

    def test_rule_no_classes(self):
        """Testet 'no_classes' Regel."""
        rule = next(r for r in RULES if r.id == "no_classes")

        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.SCHOOL_YEAR_START,
            classes=[]
        )

        suggestion = rule.evaluate(ctx)
        assert suggestion is not None
        assert suggestion.id == "create_first_class"

        # Mit Klassen sollte keine Suggestion kommen
        ctx.classes.append(ClassSummary(
            class_id="1", name="7a", grade_level=7, student_count=25, subject="Deutsch"
        ))
        assert rule.evaluate(ctx) is None

    def test_rule_phase_restriction(self):
        """Testet Phasen-Einschränkung."""
        rule = next(r for r in RULES if r.id == "no_classes")

        # In erlaubter Phase
        ctx = TeacherContext(
            teacher_id="test",
            school_id="test",
            school_year_id="test",
            current_phase=SchoolYearPhase.SCHOOL_YEAR_START,
            classes=[]
        )
        assert rule.evaluate(ctx) is not None

        # In nicht erlaubter Phase
        ctx.current_phase = SchoolYearPhase.PERFORMANCE_1
        assert rule.evaluate(ctx) is None


class TestStateEngineAPI:
    """Tests für State Engine API."""

    def test_get_context(self):
        """Testet Context-Abruf."""
        response = client.get("/api/state/context?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "context" in data
        assert "phase_info" in data

    def test_get_phase(self):
        """Testet Phasen-Abruf."""
        response = client.get("/api/state/phase?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "current_phase" in data
        assert "phase_info" in data

    def test_get_all_phases(self):
        """Testet Abruf aller Phasen."""
        response = client.get("/api/state/phases")

        assert response.status_code == 200
        data = response.json()
        assert "phases" in data
        assert len(data["phases"]) >= 8

    def test_get_suggestions(self):
        """Testet Vorschläge-Abruf."""
        response = client.get("/api/state/suggestions?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "suggestions" in data
        assert "current_phase" in data
        assert "priority_counts" in data

    def test_get_top_suggestion(self):
        """Testet Top-Vorschlag-Abruf."""
        response = client.get("/api/state/suggestions/top?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "suggestion" in data or "message" in data

    def test_get_dashboard(self):
        """Testet Dashboard-Abruf."""
        response = client.get("/api/state/dashboard?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "context" in data
        assert "suggestions" in data
        assert "stats" in data
        assert "progress" in data
        assert "phases" in data

    def test_complete_milestone(self):
        """Testet Meilenstein-Abschluss."""
        response = client.post(
            "/api/state/milestone?teacher_id=milestone-test",
            json={"milestone": "test_milestone"}
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["milestone"] == "test_milestone"
        assert "test_milestone" in data["completed_milestones"]

    def test_get_next_phase(self):
        """Testet nächste Phase."""
        response = client.get("/api/state/next-phase?teacher_id=test-api")

        assert response.status_code == 200
        data = response.json()
        assert "current_phase" in data
        assert "next_phase" in data or "message" in data


class TestDemoEndpoints:
    """Tests für Demo-Endpoints."""

    def test_demo_add_class(self):
        """Testet Demo-Klasse hinzufügen."""
        response = client.post(
            "/api/state/demo/add-class?teacher_id=demo-test&name=7a&grade_level=7&student_count=25"
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["classes"] >= 1

    def test_demo_add_event(self):
        """Testet Demo-Event hinzufügen."""
        response = client.post(
            "/api/state/demo/add-event?teacher_id=demo-test&event_type=exam&title=Mathe-Test&in_days=7"
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True

    def test_demo_update_stats(self):
        """Testet Demo-Stats aktualisieren."""
        response = client.post(
            "/api/state/demo/update-stats?teacher_id=demo-test&learning_units=5&exams_scheduled=3"
        )

        assert response.status_code == 200
        data = response.json()
        assert data["stats"]["learning_units_created"] == 5
        assert data["stats"]["exams_scheduled"] == 3

    def test_demo_reset(self):
        """Testet Demo-Reset."""
        response = client.post("/api/state/demo/reset?teacher_id=demo-test")

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True


class TestPhaseTransitions:
    """Tests für Phasen-Übergänge."""

    def test_manual_transition(self):
        """Testet manuellen Übergang."""
        # Erst Kontext mit allen Voraussetzungen erstellen
        client.post(
            "/api/state/milestone?teacher_id=transition-test",
            json={"milestone": "school_select"}
        )
        client.post(
            "/api/state/milestone?teacher_id=transition-test",
            json={"milestone": "consent_accept"}
        )
        client.post(
            "/api/state/milestone?teacher_id=transition-test",
            json={"milestone": "profile_complete"}
        )

        # Transition sollte automatisch erfolgt sein oder manuell möglich
        response = client.get("/api/state/phase?teacher_id=transition-test")
        assert response.status_code == 200

    def test_invalid_transition(self):
        """Testet ungültigen Übergang."""
        response = client.post(
            "/api/state/transition?teacher_id=invalid-test",
            json={"target_phase": "archived"}  # Nicht erlaubt
        )

        assert response.status_code == 400

    def test_invalid_phase_name(self):
        """Testet ungültigen Phasen-Namen."""
        response = client.post(
            "/api/state/transition?teacher_id=test",
            json={"target_phase": "invalid_phase"}
        )

        assert response.status_code == 400
