"""
Klausur-Korrektur API - REST API für Abitur-Klausur-Korrektur.

Zwei Modi:
- Modus A: Landes-Abitur Niedersachsen (NiBiS-Aufgaben, rechtlich geklärt)
- Modus B: Vorabitur (Lehrer-erstellte Klausuren mit Rights-Gate)

Features:
- Multi-Kriterien-Bewertung (Rechtschreibung, Grammatik, Inhalt, Struktur, Stil)
- Rights-Gate für Textquellen-Verifikation
- KI-generierter Erwartungshorizont
- Gutachten-Generierung
- 15-Punkte-Notensystem (Abitur)
- Erst-/Zweitprüfer-Workflow
- Fairness-Vergleich
"""

import logging
import uuid
import os
from datetime import datetime
from typing import List, Dict, Any, Optional
from enum import Enum
from pathlib import Path
from dataclasses import dataclass, field, asdict

from fastapi import APIRouter, HTTPException, UploadFile, File, Form, BackgroundTasks
from fastapi.responses import Response, FileResponse
from pydantic import BaseModel, Field

# FileProcessor requires OpenCV with libGL - make optional for CI
try:
    from services.file_processor import FileProcessor
    _ocr_available = True
except (ImportError, OSError):
    FileProcessor = None  # type: ignore
    _ocr_available = False

# PDF service requires WeasyPrint with system libraries - make optional for CI
try:
    from services.pdf_service import PDFService
    _pdf_available = True
except (ImportError, OSError):
    PDFService = None  # type: ignore
    _pdf_available = False

logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/klausur-korrektur",
    tags=["klausur-korrektur"],
)

# Upload directory
UPLOAD_DIR = Path("/tmp/klausur-korrektur")
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)


# ============================================================================
# Enums
# ============================================================================

class KlausurModus(str, Enum):
    """Klausur-Modus."""
    LANDES_ABITUR = "landes_abitur"  # NiBiS-Aufgaben
    VORABITUR = "vorabitur"          # Lehrer-erstellt


class TextSourceType(str, Enum):
    """Art der Textquelle."""
    NIBIS = "nibis"          # Offizielle NiBiS-Aufgabe
    EIGENTEXT = "eigentext"  # Eigener Text
    FREMDTEXT = "fremdtext"  # Fremder Text (erfordert Rights-Gate)


class TextSourceStatus(str, Enum):
    """Status der Textquellen-Verifikation."""
    PENDING = "pending"
    VERIFIED = "verified"
    REJECTED = "rejected"


class StudentKlausurStatus(str, Enum):
    """Status einer Schüler-Klausur."""
    UPLOADED = "uploaded"
    OCR_PROCESSING = "ocr_processing"
    OCR_COMPLETE = "ocr_complete"
    ANALYZING = "analyzing"
    FIRST_EXAMINER = "first_examiner"
    SECOND_EXAMINER = "second_examiner"
    COMPLETED = "completed"
    ERROR = "error"


class KlausurStatus(str, Enum):
    """Status einer Klausur."""
    DRAFT = "draft"
    TEXT_SOURCE = "text_source"
    RIGHTS_GATE = "rights_gate"
    ERWARTUNGSHORIZONT = "erwartungshorizont"
    COLLECTING = "collecting"
    CORRECTING = "correcting"
    COMPLETED = "completed"


class Anforderungsbereich(int, Enum):
    """Anforderungsbereich nach KMK."""
    I = 1    # Reproduktion
    II = 2   # Reorganisation und Transfer
    III = 3  # Reflexion und Problemlösung


# ============================================================================
# 15-Punkte-Notenschlüssel
# ============================================================================

GRADE_THRESHOLDS = {
    15: 95,   # 1+
    14: 90,   # 1
    13: 85,   # 1-
    12: 80,   # 2+
    11: 75,   # 2
    10: 70,   # 2-
    9: 65,    # 3+
    8: 60,    # 3
    7: 55,    # 3-
    6: 50,    # 4+
    5: 45,    # 4
    4: 40,    # 4-
    3: 33,    # 5+
    2: 27,    # 5
    1: 20,    # 5-
    0: 0,     # 6
}

GRADE_LABELS = {
    15: "1+ (sehr gut plus)",
    14: "1 (sehr gut)",
    13: "1- (sehr gut minus)",
    12: "2+ (gut plus)",
    11: "2 (gut)",
    10: "2- (gut minus)",
    9: "3+ (befriedigend plus)",
    8: "3 (befriedigend)",
    7: "3- (befriedigend minus)",
    6: "4+ (ausreichend plus)",
    5: "4 (ausreichend)",
    4: "4- (ausreichend minus)",
    3: "5+ (mangelhaft plus)",
    2: "5 (mangelhaft)",
    1: "5- (mangelhaft minus)",
    0: "6 (ungenügend)",
}

# Standard-Bewertungskriterien
DEFAULT_CRITERIA = {
    "rechtschreibung": {"weight": 0.15, "max": 100, "label": "Rechtschreibung"},
    "grammatik": {"weight": 0.15, "max": 100, "label": "Grammatik"},
    "inhalt": {"weight": 0.40, "max": 100, "label": "Inhalt"},
    "struktur": {"weight": 0.15, "max": 100, "label": "Struktur"},
    "stil": {"weight": 0.15, "max": 100, "label": "Stil"},
}

# Operatoren nach Niedersachsen KC
OPERATORS = {
    "I": [
        {"name": "nennen", "description": "Informationen ohne Erläuterung wiedergeben"},
        {"name": "beschreiben", "description": "Sachverhalte in eigenen Worten wiedergeben"},
        {"name": "wiedergeben", "description": "Inhalte in eigenen Worten darstellen"},
        {"name": "zusammenfassen", "description": "Wesentliche Aspekte komprimiert darstellen"},
    ],
    "II": [
        {"name": "analysieren", "description": "Materialien systematisch untersuchen"},
        {"name": "erklären", "description": "Sachverhalte verständlich machen"},
        {"name": "erläutern", "description": "Sachverhalte mit Beispielen veranschaulichen"},
        {"name": "vergleichen", "description": "Gemeinsamkeiten und Unterschiede herausarbeiten"},
        {"name": "einordnen", "description": "In einen Zusammenhang stellen"},
        {"name": "charakterisieren", "description": "Wesenszüge herausarbeiten"},
    ],
    "III": [
        {"name": "beurteilen", "description": "Aussagen an Kriterien messen und ein Urteil fällen"},
        {"name": "bewerten", "description": "Eine eigene Position mit Begründung einnehmen"},
        {"name": "erörtern", "description": "Ein Thema multiperspektivisch diskutieren"},
        {"name": "Stellung nehmen", "description": "Eigene Position argumentativ vertreten"},
        {"name": "gestalten", "description": "Kreativ-produktive Texte erstellen"},
        {"name": "entwerfen", "description": "Konzepte entwickeln"},
    ],
}


# ============================================================================
# Pydantic Models für API Requests/Responses
# ============================================================================

class TextSourceCreate(BaseModel):
    """Request zum Erstellen einer Textquelle."""
    source_type: TextSourceType
    title: str
    author: str = ""
    content: str
    nibis_id: Optional[str] = None  # Für NiBiS-Aufgaben
    license_info: Optional[Dict[str, Any]] = None


class TextSourceResponse(BaseModel):
    """Response für eine Textquelle."""
    id: str
    source_type: TextSourceType
    title: str
    author: str
    content: str
    nibis_id: Optional[str]
    license_status: TextSourceStatus
    license_info: Optional[Dict[str, Any]]
    created_at: datetime


class AufgabeCreate(BaseModel):
    """Request zum Erstellen einer Aufgabe."""
    nummer: str
    text: str
    operator: str
    anforderungsbereich: int = Field(ge=1, le=3)
    erwartete_leistungen: List[str] = []
    punkte: int = Field(ge=0)


class AufgabeResponse(BaseModel):
    """Response für eine Aufgabe."""
    id: str
    nummer: str
    text: str
    operator: str
    anforderungsbereich: int
    erwartete_leistungen: List[str]
    punkte: int


class ErwartungshorizontCreate(BaseModel):
    """Request zum Erstellen/Aktualisieren eines Erwartungshorizonts."""
    aufgaben: List[AufgabeCreate]
    max_points: int = Field(ge=0)
    hinweise: str = ""


class ErwartungshorizontResponse(BaseModel):
    """Response für einen Erwartungshorizont."""
    id: str
    aufgaben: List[AufgabeResponse]
    max_points: int
    hinweise: str
    generated: bool
    created_at: datetime


class CriterionScoreUpdate(BaseModel):
    """Update für einen Kriterien-Score."""
    score: int = Field(ge=0, le=100)
    annotations: List[str] = []
    comment: str = ""


class AnnotationCreate(BaseModel):
    """Request zum Erstellen einer Annotation."""
    page: int
    x: float
    y: float
    width: float
    height: float
    text: str
    type: str = "comment"  # comment, correction, highlight
    color: str = "#FF0000"


class AnnotationResponse(BaseModel):
    """Response für eine Annotation."""
    id: str
    page: int
    x: float
    y: float
    width: float
    height: float
    text: str
    type: str
    color: str
    created_at: datetime


class GutachtenCreate(BaseModel):
    """Request zum Erstellen/Aktualisieren eines Gutachtens."""
    einleitung: str
    hauptteil: str
    fazit: str
    staerken: List[str] = []
    schwaechen: List[str] = []


class GutachtenResponse(BaseModel):
    """Response für ein Gutachten."""
    id: str
    einleitung: str
    hauptteil: str
    fazit: str
    staerken: List[str]
    schwaechen: List[str]
    generated: bool
    edited: bool
    created_at: datetime


class ExaminerResultCreate(BaseModel):
    """Request für Prüfer-Ergebnis."""
    examiner_id: str
    examiner_name: str
    grade_points: int = Field(ge=0, le=15)
    comment: str = ""


class ExaminerResultResponse(BaseModel):
    """Response für Prüfer-Ergebnis."""
    examiner_id: str
    examiner_name: str
    grade_points: int
    comment: str
    submitted_at: datetime


class StudentKlausurCreate(BaseModel):
    """Request zum Erstellen einer Schüler-Klausur."""
    student_name: str
    student_id: Optional[str] = None


class StudentKlausurResponse(BaseModel):
    """Response für eine Schüler-Klausur."""
    id: str
    student_name: str
    student_id: Optional[str]
    file_path: Optional[str]
    file_name: Optional[str]
    ocr_text: Optional[str]
    status: StudentKlausurStatus
    criteria_scores: Dict[str, Dict[str, Any]]
    annotations: List[AnnotationResponse]
    gutachten: Optional[GutachtenResponse]
    raw_points: int
    grade_points: int
    grade_label: str
    first_examiner: Optional[ExaminerResultResponse]
    second_examiner: Optional[ExaminerResultResponse]
    created_at: datetime
    updated_at: datetime


class KlausurCreate(BaseModel):
    """Request zum Erstellen einer Klausur."""
    title: str
    subject: str
    modus: KlausurModus
    year: int = Field(ge=2020, le=2100)
    semester: str  # Q1, Q2, Q3, Q4
    kurs: str = ""  # z.B. "Deutsch LK", "Englisch GK"
    class_id: Optional[str] = None  # ID der zugeordneten Klasse


class KlausurUpdate(BaseModel):
    """Request zum Aktualisieren einer Klausur."""
    title: Optional[str] = None
    subject: Optional[str] = None
    kurs: Optional[str] = None
    status: Optional[KlausurStatus] = None


class KlausurResponse(BaseModel):
    """Response für eine Klausur."""
    id: str
    title: str
    subject: str
    modus: KlausurModus
    year: int
    semester: str
    kurs: str
    class_id: Optional[str]
    status: KlausurStatus
    text_sources: List[TextSourceResponse]
    erwartungshorizont: Optional[ErwartungshorizontResponse]
    student_count: int
    completed_count: int
    average_grade: Optional[float]
    created_at: datetime
    updated_at: datetime


class NiBiSAufgabe(BaseModel):
    """NiBiS-Aufgabe aus dem Katalog."""
    id: str
    year: int
    subject: str
    title: str
    type: str  # "schriftlich", "mündlich"
    text_preview: str
    download_url: Optional[str]


class FairnessReport(BaseModel):
    """Fairness-Bericht für eine Klausur."""
    klausur_id: str
    total_students: int
    average_grade: float
    grade_distribution: Dict[int, int]
    criteria_averages: Dict[str, float]
    outliers: List[Dict[str, Any]]
    recommendations: List[str]


# ============================================================================
# Internal Data Classes (In-Memory Storage)
# ============================================================================

@dataclass
class TextSource:
    """Textquelle für Klausur."""
    id: str
    source_type: TextSourceType
    title: str
    author: str
    content: str
    nibis_id: Optional[str]
    license_status: TextSourceStatus
    license_info: Optional[Dict[str, Any]]
    created_at: datetime


@dataclass
class Aufgabe:
    """Aufgabe im Erwartungshorizont."""
    id: str
    nummer: str
    text: str
    operator: str
    anforderungsbereich: int
    erwartete_leistungen: List[str]
    punkte: int


@dataclass
class Erwartungshorizont:
    """Erwartungshorizont für Klausur."""
    id: str
    aufgaben: List[Aufgabe]
    max_points: int
    hinweise: str
    generated: bool
    created_at: datetime


@dataclass
class Annotation:
    """Overlay-Annotation auf Dokument."""
    id: str
    page: int
    x: float
    y: float
    width: float
    height: float
    text: str
    type: str
    color: str
    created_at: datetime


@dataclass
class Gutachten:
    """Gutachten für Schüler-Klausur."""
    id: str
    einleitung: str
    hauptteil: str
    fazit: str
    staerken: List[str]
    schwaechen: List[str]
    generated: bool
    edited: bool
    created_at: datetime


@dataclass
class ExaminerResult:
    """Prüfer-Ergebnis."""
    examiner_id: str
    examiner_name: str
    grade_points: int
    comment: str
    submitted_at: datetime


@dataclass
class CriterionScore:
    """Bewertung für ein Kriterium."""
    score: int
    weight: float
    annotations: List[str]
    comment: str
    ai_suggestions: List[str]


@dataclass
class StudentKlausur:
    """Schüler-Klausur."""
    id: str
    student_name: str
    student_id: Optional[str]
    file_path: Optional[str]
    file_name: Optional[str]
    ocr_text: Optional[str]
    status: StudentKlausurStatus
    criteria_scores: Dict[str, CriterionScore]
    annotations: List[Annotation]
    gutachten: Optional[Gutachten]
    raw_points: int
    grade_points: int
    first_examiner: Optional[ExaminerResult]
    second_examiner: Optional[ExaminerResult]
    created_at: datetime
    updated_at: datetime


@dataclass
class AbiturKlausur:
    """Abitur-Klausur."""
    id: str
    title: str
    subject: str
    modus: KlausurModus
    year: int
    semester: str
    kurs: str
    class_id: Optional[str]  # ID der zugeordneten Klasse
    status: KlausurStatus
    text_sources: List[TextSource]
    erwartungshorizont: Optional[Erwartungshorizont]
    students: List[StudentKlausur]
    created_at: datetime
    updated_at: datetime


# ============================================================================
# In-Memory Storage
# ============================================================================

_klausuren: Dict[str, AbiturKlausur] = {}

# NiBiS-Katalog (Demo-Daten)
_nibis_katalog: Dict[str, NiBiSAufgabe] = {
    "nibis-2024-deutsch-01": NiBiSAufgabe(
        id="nibis-2024-deutsch-01",
        year=2024,
        subject="Deutsch",
        title="Abitur 2024 - Aufgabenstellung I (Lyrik)",
        type="schriftlich",
        text_preview="Analysieren Sie das Gedicht von Else Lasker-Schüler...",
        download_url=None
    ),
    "nibis-2024-deutsch-02": NiBiSAufgabe(
        id="nibis-2024-deutsch-02",
        year=2024,
        subject="Deutsch",
        title="Abitur 2024 - Aufgabenstellung II (Drama)",
        type="schriftlich",
        text_preview="Analysieren Sie den Szenenausschnitt aus 'Faust I'...",
        download_url=None
    ),
    "nibis-2024-deutsch-03": NiBiSAufgabe(
        id="nibis-2024-deutsch-03",
        year=2024,
        subject="Deutsch",
        title="Abitur 2024 - Aufgabenstellung III (Epik)",
        type="schriftlich",
        text_preview="Analysieren Sie den Romanausschnitt aus 'Der Prozess'...",
        download_url=None
    ),
    "nibis-2023-deutsch-01": NiBiSAufgabe(
        id="nibis-2023-deutsch-01",
        year=2023,
        subject="Deutsch",
        title="Abitur 2023 - Aufgabenstellung I (Lyrik)",
        type="schriftlich",
        text_preview="Analysieren Sie das Gedicht 'Mondnacht' von Eichendorff...",
        download_url=None
    ),
    "nibis-2024-englisch-01": NiBiSAufgabe(
        id="nibis-2024-englisch-01",
        year=2024,
        subject="Englisch",
        title="Abitur 2024 - Reading Comprehension",
        type="schriftlich",
        text_preview="Read the following article about climate change...",
        download_url=None
    ),
}


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

def calculate_15_point_grade(percentage: float) -> int:
    """Berechnet 15-Punkte-Note aus Prozent."""
    for points, threshold in sorted(GRADE_THRESHOLDS.items(), reverse=True):
        if percentage >= threshold:
            return points
    return 0


def calculate_raw_points(criteria_scores: Dict[str, CriterionScore], max_points: int) -> int:
    """Berechnet Rohpunkte aus Kriterien-Scores."""
    if not criteria_scores:
        return 0

    weighted_sum = 0.0
    total_weight = 0.0

    for criterion, score in criteria_scores.items():
        weighted_sum += score.score * score.weight
        total_weight += score.weight

    if total_weight == 0:
        return 0

    percentage = weighted_sum / total_weight
    return int(max_points * percentage / 100)


def get_grade_label(grade_points: int) -> str:
    """Gibt Label für Note zurück."""
    return GRADE_LABELS.get(grade_points, "unbekannt")


def _to_text_source_response(ts: TextSource) -> TextSourceResponse:
    """Konvertiert TextSource zu Response."""
    return TextSourceResponse(
        id=ts.id,
        source_type=ts.source_type,
        title=ts.title,
        author=ts.author,
        content=ts.content,
        nibis_id=ts.nibis_id,
        license_status=ts.license_status,
        license_info=ts.license_info,
        created_at=ts.created_at
    )


def _to_aufgabe_response(a: Aufgabe) -> AufgabeResponse:
    """Konvertiert Aufgabe zu Response."""
    return AufgabeResponse(
        id=a.id,
        nummer=a.nummer,
        text=a.text,
        operator=a.operator,
        anforderungsbereich=a.anforderungsbereich,
        erwartete_leistungen=a.erwartete_leistungen,
        punkte=a.punkte
    )


def _to_erwartungshorizont_response(eh: Erwartungshorizont) -> ErwartungshorizontResponse:
    """Konvertiert Erwartungshorizont zu Response."""
    return ErwartungshorizontResponse(
        id=eh.id,
        aufgaben=[_to_aufgabe_response(a) for a in eh.aufgaben],
        max_points=eh.max_points,
        hinweise=eh.hinweise,
        generated=eh.generated,
        created_at=eh.created_at
    )


def _to_annotation_response(a: Annotation) -> AnnotationResponse:
    """Konvertiert Annotation zu Response."""
    return AnnotationResponse(
        id=a.id,
        page=a.page,
        x=a.x,
        y=a.y,
        width=a.width,
        height=a.height,
        text=a.text,
        type=a.type,
        color=a.color,
        created_at=a.created_at
    )


def _to_gutachten_response(g: Gutachten) -> GutachtenResponse:
    """Konvertiert Gutachten zu Response."""
    return GutachtenResponse(
        id=g.id,
        einleitung=g.einleitung,
        hauptteil=g.hauptteil,
        fazit=g.fazit,
        staerken=g.staerken,
        schwaechen=g.schwaechen,
        generated=g.generated,
        edited=g.edited,
        created_at=g.created_at
    )


def _to_examiner_response(e: ExaminerResult) -> ExaminerResultResponse:
    """Konvertiert ExaminerResult zu Response."""
    return ExaminerResultResponse(
        examiner_id=e.examiner_id,
        examiner_name=e.examiner_name,
        grade_points=e.grade_points,
        comment=e.comment,
        submitted_at=e.submitted_at
    )


def _to_student_klausur_response(sk: StudentKlausur) -> StudentKlausurResponse:
    """Konvertiert StudentKlausur zu Response."""
    return StudentKlausurResponse(
        id=sk.id,
        student_name=sk.student_name,
        student_id=sk.student_id,
        file_path=sk.file_path,
        file_name=sk.file_name,
        ocr_text=sk.ocr_text,
        status=sk.status,
        criteria_scores={
            k: {"score": v.score, "weight": v.weight, "annotations": v.annotations,
                "comment": v.comment, "ai_suggestions": v.ai_suggestions}
            for k, v in sk.criteria_scores.items()
        },
        annotations=[_to_annotation_response(a) for a in sk.annotations],
        gutachten=_to_gutachten_response(sk.gutachten) if sk.gutachten else None,
        raw_points=sk.raw_points,
        grade_points=sk.grade_points,
        grade_label=get_grade_label(sk.grade_points),
        first_examiner=_to_examiner_response(sk.first_examiner) if sk.first_examiner else None,
        second_examiner=_to_examiner_response(sk.second_examiner) if sk.second_examiner else None,
        created_at=sk.created_at,
        updated_at=sk.updated_at
    )


def _to_klausur_response(k: AbiturKlausur) -> KlausurResponse:
    """Konvertiert AbiturKlausur zu Response."""
    completed = [s for s in k.students if s.status == StudentKlausurStatus.COMPLETED]
    avg_grade = None
    if completed:
        avg_grade = sum(s.grade_points for s in completed) / len(completed)

    return KlausurResponse(
        id=k.id,
        title=k.title,
        subject=k.subject,
        modus=k.modus,
        year=k.year,
        semester=k.semester,
        kurs=k.kurs,
        class_id=k.class_id,
        status=k.status,
        text_sources=[_to_text_source_response(ts) for ts in k.text_sources],
        erwartungshorizont=_to_erwartungshorizont_response(k.erwartungshorizont) if k.erwartungshorizont else None,
        student_count=len(k.students),
        completed_count=len(completed),
        average_grade=avg_grade,
        created_at=k.created_at,
        updated_at=k.updated_at
    )


# ============================================================================
# Background Tasks
# ============================================================================

async def _process_ocr(klausur_id: str, student_id: str, file_path: str):
    """Background Task für OCR-Verarbeitung."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        return

    student = next((s for s in klausur.students if s.id == student_id), None)
    if not student:
        return

    try:
        student.status = StudentKlausurStatus.OCR_PROCESSING
        student.updated_at = datetime.utcnow()

        # OCR durchführen
        processor = FileProcessor()
        result = processor.process_file(file_path)

        if result.success and result.text:
            student.ocr_text = result.text
            student.status = StudentKlausurStatus.OCR_COMPLETE
            logger.info(f"OCR completed for {student_id}: {len(result.text)} characters")
        else:
            # Bei OCR-Fehlschlag auf UPLOADED zurücksetzen, damit manuell weitergearbeitet werden kann
            student.ocr_text = None
            student.status = StudentKlausurStatus.UPLOADED
            logger.warning(f"OCR failed for {student_id}, keeping status as UPLOADED")

        student.updated_at = datetime.utcnow()

    except Exception as e:
        logger.error(f"OCR error for {student_id}: {e}")
        # Bei Fehlern auf UPLOADED zurücksetzen, nicht ERROR
        student.status = StudentKlausurStatus.UPLOADED
        student.ocr_text = None
        student.updated_at = datetime.utcnow()


# ============================================================================
# API Endpoints - Klausuren
# ============================================================================

@router.post("/klausuren", response_model=KlausurResponse)
async def create_klausur(data: KlausurCreate):
    """Erstellt eine neue Klausur."""
    klausur_id = str(uuid.uuid4())
    now = datetime.utcnow()

    klausur = AbiturKlausur(
        id=klausur_id,
        title=data.title,
        subject=data.subject,
        modus=data.modus,
        year=data.year,
        semester=data.semester,
        kurs=data.kurs,
        class_id=data.class_id,
        status=KlausurStatus.DRAFT,
        text_sources=[],
        erwartungshorizont=None,
        students=[],
        created_at=now,
        updated_at=now
    )

    _klausuren[klausur_id] = klausur
    logger.info(f"Created klausur {klausur_id}: {data.title}")

    return _to_klausur_response(klausur)


@router.get("/klausuren", response_model=List[KlausurResponse])
async def list_klausuren(
    modus: Optional[KlausurModus] = None,
    subject: Optional[str] = None,
    year: Optional[int] = None,
    status: Optional[KlausurStatus] = None
):
    """Listet alle Klausuren auf."""
    klausuren = list(_klausuren.values())

    if modus:
        klausuren = [k for k in klausuren if k.modus == modus]
    if subject:
        klausuren = [k for k in klausuren if k.subject == subject]
    if year:
        klausuren = [k for k in klausuren if k.year == year]
    if status:
        klausuren = [k for k in klausuren if k.status == status]

    klausuren.sort(key=lambda x: x.created_at, reverse=True)
    return [_to_klausur_response(k) for k in klausuren]


@router.get("/klausuren/{klausur_id}", response_model=KlausurResponse)
async def get_klausur(klausur_id: str):
    """Ruft eine Klausur ab."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    return _to_klausur_response(klausur)


@router.put("/klausuren/{klausur_id}", response_model=KlausurResponse)
async def update_klausur(klausur_id: str, data: KlausurUpdate):
    """Aktualisiert eine Klausur."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    if data.title is not None:
        klausur.title = data.title
    if data.subject is not None:
        klausur.subject = data.subject
    if data.kurs is not None:
        klausur.kurs = data.kurs
    if data.status is not None:
        klausur.status = data.status

    klausur.updated_at = datetime.utcnow()
    return _to_klausur_response(klausur)


@router.delete("/klausuren/{klausur_id}")
async def delete_klausur(klausur_id: str):
    """Löscht eine Klausur."""
    if klausur_id not in _klausuren:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    klausur = _klausuren[klausur_id]

    # Lösche hochgeladene Dateien
    for student in klausur.students:
        if student.file_path and os.path.exists(student.file_path):
            try:
                os.remove(student.file_path)
            except Exception as e:
                logger.warning(f"Could not delete file {student.file_path}: {e}")

    del _klausuren[klausur_id]
    logger.info(f"Deleted klausur {klausur_id}")

    return {"status": "deleted", "id": klausur_id}


# ============================================================================
# API Endpoints - Text-Quellen
# ============================================================================

@router.post("/klausuren/{klausur_id}/text-sources", response_model=TextSourceResponse)
async def add_text_source(klausur_id: str, data: TextSourceCreate):
    """Fügt eine Textquelle zur Klausur hinzu."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    ts_id = str(uuid.uuid4())
    now = datetime.utcnow()

    # Status basierend auf Typ setzen
    if data.source_type == TextSourceType.NIBIS:
        license_status = TextSourceStatus.VERIFIED
    elif data.source_type == TextSourceType.EIGENTEXT:
        license_status = TextSourceStatus.VERIFIED
    else:
        license_status = TextSourceStatus.PENDING

    text_source = TextSource(
        id=ts_id,
        source_type=data.source_type,
        title=data.title,
        author=data.author,
        content=data.content,
        nibis_id=data.nibis_id,
        license_status=license_status,
        license_info=data.license_info,
        created_at=now
    )

    klausur.text_sources.append(text_source)
    klausur.status = KlausurStatus.TEXT_SOURCE
    klausur.updated_at = now

    logger.info(f"Added text source {ts_id} to klausur {klausur_id}")
    return _to_text_source_response(text_source)


@router.post("/text-sources/{text_source_id}/verify", response_model=TextSourceResponse)
async def verify_text_source(text_source_id: str, license_info: Dict[str, Any] = None):
    """Verifiziert eine Textquelle (Rights-Gate)."""
    # Finde Textquelle
    for klausur in _klausuren.values():
        for ts in klausur.text_sources:
            if ts.id == text_source_id:
                ts.license_status = TextSourceStatus.VERIFIED
                ts.license_info = license_info or {"verified_at": datetime.utcnow().isoformat()}

                # Prüfe ob alle Textquellen verifiziert
                all_verified = all(
                    t.license_status == TextSourceStatus.VERIFIED
                    for t in klausur.text_sources
                )
                if all_verified and klausur.text_sources:
                    klausur.status = KlausurStatus.ERWARTUNGSHORIZONT

                klausur.updated_at = datetime.utcnow()
                return _to_text_source_response(ts)

    raise HTTPException(status_code=404, detail="Textquelle nicht gefunden")


@router.delete("/klausuren/{klausur_id}/text-sources/{text_source_id}")
async def delete_text_source(klausur_id: str, text_source_id: str):
    """Löscht eine Textquelle."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    klausur.text_sources = [ts for ts in klausur.text_sources if ts.id != text_source_id]
    klausur.updated_at = datetime.utcnow()

    return {"status": "deleted", "id": text_source_id}


# ============================================================================
# API Endpoints - NiBiS-Katalog
# ============================================================================

@router.get("/nibis/aufgaben", response_model=List[NiBiSAufgabe])
async def list_nibis_aufgaben(
    subject: Optional[str] = None,
    year: Optional[int] = None
):
    """Listet NiBiS-Aufgaben aus dem Katalog auf."""
    aufgaben = list(_nibis_katalog.values())

    if subject:
        aufgaben = [a for a in aufgaben if a.subject.lower() == subject.lower()]
    if year:
        aufgaben = [a for a in aufgaben if a.year == year]

    aufgaben.sort(key=lambda x: (x.year, x.subject), reverse=True)
    return aufgaben


@router.get("/nibis/aufgaben/{aufgabe_id}", response_model=NiBiSAufgabe)
async def get_nibis_aufgabe(aufgabe_id: str):
    """Ruft eine NiBiS-Aufgabe ab."""
    aufgabe = _nibis_katalog.get(aufgabe_id)
    if not aufgabe:
        raise HTTPException(status_code=404, detail="NiBiS-Aufgabe nicht gefunden")
    return aufgabe


# ============================================================================
# API Endpoints - Erwartungshorizont
# ============================================================================

@router.post("/klausuren/{klausur_id}/erwartungshorizont/generate", response_model=ErwartungshorizontResponse)
async def generate_erwartungshorizont(klausur_id: str):
    """Generiert KI-basierten Erwartungshorizont."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    if not klausur.text_sources:
        raise HTTPException(status_code=400, detail="Keine Textquellen vorhanden")

    # Kombiniere alle Texte
    combined_text = "\n\n".join(ts.content for ts in klausur.text_sources)

    # Generiere Demo-Erwartungshorizont (in Produktion: KI-generiert)
    eh_id = str(uuid.uuid4())
    now = datetime.utcnow()

    aufgaben = [
        Aufgabe(
            id=str(uuid.uuid4()),
            nummer="1",
            text="Analysieren Sie die sprachlichen und stilistischen Mittel des Textes.",
            operator="analysieren",
            anforderungsbereich=2,
            erwartete_leistungen=[
                "Erkennung von Metaphern und deren Wirkung",
                "Analyse der Syntax und Satzstruktur",
                "Einordnung in den historischen Kontext"
            ],
            punkte=30
        ),
        Aufgabe(
            id=str(uuid.uuid4()),
            nummer="2",
            text="Erörtern Sie die Aktualität des Themas in Bezug auf die Gegenwart.",
            operator="erörtern",
            anforderungsbereich=3,
            erwartete_leistungen=[
                "Herausarbeitung der zentralen Thesen",
                "Vergleich mit aktuellen gesellschaftlichen Entwicklungen",
                "Differenzierte Stellungnahme mit Begründung"
            ],
            punkte=40
        ),
        Aufgabe(
            id=str(uuid.uuid4()),
            nummer="3",
            text="Verfassen Sie einen Kommentar zum Thema aus persönlicher Perspektive.",
            operator="gestalten",
            anforderungsbereich=3,
            erwartete_leistungen=[
                "Einhaltung der Textsortenmerkmale (Kommentar)",
                "Klare Positionierung und Argumentation",
                "Sprachliche Angemessenheit und Stilsicherheit"
            ],
            punkte=30
        )
    ]

    erwartungshorizont = Erwartungshorizont(
        id=eh_id,
        aufgaben=aufgaben,
        max_points=100,
        hinweise="Der Erwartungshorizont dient als Orientierung. Abweichende, aber schlüssige Lösungen sind möglich.",
        generated=True,
        created_at=now
    )

    klausur.erwartungshorizont = erwartungshorizont
    klausur.status = KlausurStatus.COLLECTING
    klausur.updated_at = now

    logger.info(f"Generated erwartungshorizont for klausur {klausur_id}")
    return _to_erwartungshorizont_response(erwartungshorizont)


@router.put("/klausuren/{klausur_id}/erwartungshorizont", response_model=ErwartungshorizontResponse)
async def update_erwartungshorizont(klausur_id: str, data: ErwartungshorizontCreate):
    """Aktualisiert den Erwartungshorizont."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    now = datetime.utcnow()

    aufgaben = [
        Aufgabe(
            id=str(uuid.uuid4()),
            nummer=a.nummer,
            text=a.text,
            operator=a.operator,
            anforderungsbereich=a.anforderungsbereich,
            erwartete_leistungen=a.erwartete_leistungen,
            punkte=a.punkte
        )
        for a in data.aufgaben
    ]

    if klausur.erwartungshorizont:
        klausur.erwartungshorizont.aufgaben = aufgaben
        klausur.erwartungshorizont.max_points = data.max_points
        klausur.erwartungshorizont.hinweise = data.hinweise
    else:
        klausur.erwartungshorizont = Erwartungshorizont(
            id=str(uuid.uuid4()),
            aufgaben=aufgaben,
            max_points=data.max_points,
            hinweise=data.hinweise,
            generated=False,
            created_at=now
        )

    klausur.updated_at = now
    return _to_erwartungshorizont_response(klausur.erwartungshorizont)


@router.get("/operators", response_model=Dict[str, List[Dict[str, str]]])
async def get_operators():
    """Gibt alle Operatoren nach Anforderungsbereich zurück."""
    return OPERATORS


# ============================================================================
# API Endpoints - Schülerarbeiten
# ============================================================================

@router.post("/klausuren/{klausur_id}/students", response_model=StudentKlausurResponse)
async def add_student(
    klausur_id: str,
    background_tasks: BackgroundTasks,
    student_name: str = Form(...),
    student_id: Optional[str] = Form(None),
    file: UploadFile = File(...)
):
    """Lädt eine Schülerarbeit hoch."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    # Validiere Dateiformat
    allowed_extensions = {".pdf", ".png", ".jpg", ".jpeg"}
    file_ext = Path(file.filename).suffix.lower() if file.filename else ""

    if file_ext not in allowed_extensions:
        raise HTTPException(
            status_code=400,
            detail=f"Ungültiges Dateiformat. Erlaubt: {', '.join(allowed_extensions)}"
        )

    sk_id = str(uuid.uuid4())
    now = datetime.utcnow()

    # Speichere Datei
    file_path = UPLOAD_DIR / f"{sk_id}{file_ext}"

    try:
        content = await file.read()
        with open(file_path, "wb") as f:
            f.write(content)
    except Exception as e:
        logger.error(f"Upload error: {e}")
        raise HTTPException(status_code=500, detail=f"Upload fehlgeschlagen: {str(e)}")

    # Initialisiere Kriterien-Scores
    criteria_scores = {
        k: CriterionScore(
            score=0,
            weight=v["weight"],
            annotations=[],
            comment="",
            ai_suggestions=[]
        )
        for k, v in DEFAULT_CRITERIA.items()
    }

    student_klausur = StudentKlausur(
        id=sk_id,
        student_name=student_name,
        student_id=student_id,
        file_path=str(file_path),
        file_name=file.filename,
        ocr_text=None,
        status=StudentKlausurStatus.UPLOADED,
        criteria_scores=criteria_scores,
        annotations=[],
        gutachten=None,
        raw_points=0,
        grade_points=0,
        first_examiner=None,
        second_examiner=None,
        created_at=now,
        updated_at=now
    )

    klausur.students.append(student_klausur)
    klausur.status = KlausurStatus.CORRECTING
    klausur.updated_at = now

    # Starte OCR im Hintergrund
    background_tasks.add_task(_process_ocr, klausur_id, sk_id, str(file_path))

    logger.info(f"Added student {student_name} to klausur {klausur_id}")
    return _to_student_klausur_response(student_klausur)


@router.get("/klausuren/{klausur_id}/students", response_model=List[StudentKlausurResponse])
async def list_students(klausur_id: str):
    """Listet alle Schülerarbeiten einer Klausur auf."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    return [_to_student_klausur_response(s) for s in klausur.students]


@router.get("/students/{student_id}", response_model=StudentKlausurResponse)
async def get_student(student_id: str):
    """Ruft eine Schülerarbeit ab."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.delete("/students/{student_id}")
async def delete_student(student_id: str):
    """Löscht eine Schülerarbeit und deren Datei."""
    for klausur in _klausuren.values():
        for i, student in enumerate(klausur.students):
            if student.id == student_id:
                # Lösche die Datei, falls vorhanden
                if student.file_path:
                    file_path = Path(student.file_path)
                    if file_path.exists():
                        try:
                            file_path.unlink()
                            logger.info(f"Deleted file: {file_path}")
                        except Exception as e:
                            logger.warning(f"Could not delete file {file_path}: {e}")

                # Entferne aus der Liste
                klausur.students.pop(i)
                klausur.updated_at = datetime.utcnow()

                logger.info(f"Deleted student work {student_id} ({student.student_name})")
                return {"success": True, "message": f"Schülerarbeit '{student.student_name}' gelöscht"}

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.get("/students/{student_id}/file")
async def get_student_file(student_id: str):
    """Liefert die Datei einer Schülerarbeit aus."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                if not student.file_path:
                    raise HTTPException(status_code=404, detail="Keine Datei vorhanden")

                file_path = Path(student.file_path)
                if not file_path.exists():
                    raise HTTPException(status_code=404, detail="Datei nicht gefunden")

                # Determine media type
                suffix = file_path.suffix.lower()
                media_types = {
                    ".pdf": "application/pdf",
                    ".png": "image/png",
                    ".jpg": "image/jpeg",
                    ".jpeg": "image/jpeg",
                    ".gif": "image/gif",
                    ".webp": "image/webp",
                }
                media_type = media_types.get(suffix, "application/octet-stream")

                return FileResponse(
                    path=str(file_path),
                    media_type=media_type,
                    filename=student.file_name or file_path.name
                )

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.post("/students/{student_id}/ocr")
async def start_ocr(student_id: str, background_tasks: BackgroundTasks):
    """Startet OCR für eine Schülerarbeit (erneut)."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                if not student.file_path:
                    raise HTTPException(status_code=400, detail="Keine Datei vorhanden")

                student.status = StudentKlausurStatus.UPLOADED
                student.ocr_text = None
                student.updated_at = datetime.utcnow()

                background_tasks.add_task(_process_ocr, klausur.id, student_id, student.file_path)

                return {"status": "started", "student_id": student_id}

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


# ============================================================================
# API Endpoints - Bewertung
# ============================================================================

@router.post("/students/{student_id}/evaluate", response_model=StudentKlausurResponse)
async def evaluate_student(student_id: str):
    """Führt KI-basierte Bewertung durch."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                if student.status not in [StudentKlausurStatus.OCR_COMPLETE, StudentKlausurStatus.ANALYZING]:
                    raise HTTPException(
                        status_code=400,
                        detail=f"OCR muss abgeschlossen sein. Aktueller Status: {student.status}"
                    )

                student.status = StudentKlausurStatus.ANALYZING

                # Demo-Bewertung (in Produktion: KI-basiert)
                import random

                for criterion in student.criteria_scores:
                    base_score = random.randint(50, 90)
                    student.criteria_scores[criterion].score = base_score
                    student.criteria_scores[criterion].ai_suggestions = [
                        f"Verbesserungsvorschlag für {criterion}"
                    ]

                # Berechne Punkte
                max_points = klausur.erwartungshorizont.max_points if klausur.erwartungshorizont else 100
                student.raw_points = calculate_raw_points(student.criteria_scores, max_points)

                percentage = (student.raw_points / max_points * 100) if max_points > 0 else 0
                student.grade_points = calculate_15_point_grade(percentage)

                student.status = StudentKlausurStatus.FIRST_EXAMINER
                student.updated_at = datetime.utcnow()

                logger.info(f"Evaluated student {student_id}: {student.grade_points} points")
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.put("/students/{student_id}/criteria", response_model=StudentKlausurResponse)
async def update_criteria(student_id: str, updates: Dict[str, CriterionScoreUpdate]):
    """Aktualisiert Kriterien-Bewertungen."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                for criterion, update in updates.items():
                    if criterion in student.criteria_scores:
                        student.criteria_scores[criterion].score = update.score
                        student.criteria_scores[criterion].annotations = update.annotations
                        student.criteria_scores[criterion].comment = update.comment

                # Neuberechnung
                max_points = klausur.erwartungshorizont.max_points if klausur.erwartungshorizont else 100
                student.raw_points = calculate_raw_points(student.criteria_scores, max_points)

                percentage = (student.raw_points / max_points * 100) if max_points > 0 else 0
                student.grade_points = calculate_15_point_grade(percentage)

                student.updated_at = datetime.utcnow()
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.post("/students/{student_id}/annotations", response_model=AnnotationResponse)
async def add_annotation(student_id: str, data: AnnotationCreate):
    """Fügt eine Annotation hinzu."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                annotation = Annotation(
                    id=str(uuid.uuid4()),
                    page=data.page,
                    x=data.x,
                    y=data.y,
                    width=data.width,
                    height=data.height,
                    text=data.text,
                    type=data.type,
                    color=data.color,
                    created_at=datetime.utcnow()
                )

                student.annotations.append(annotation)
                student.updated_at = datetime.utcnow()

                return _to_annotation_response(annotation)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.delete("/students/{student_id}/annotations/{annotation_id}")
async def delete_annotation(student_id: str, annotation_id: str):
    """Löscht eine Annotation."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                student.annotations = [a for a in student.annotations if a.id != annotation_id]
                student.updated_at = datetime.utcnow()
                return {"status": "deleted", "id": annotation_id}

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


# ============================================================================
# API Endpoints - Gutachten
# ============================================================================

@router.post("/students/{student_id}/gutachten/generate", response_model=GutachtenResponse)
async def generate_gutachten(student_id: str):
    """Generiert KI-basiertes Gutachten."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                now = datetime.utcnow()
                grade_label = get_grade_label(student.grade_points)

                # Demo-Gutachten (in Produktion: KI-generiert)
                gutachten = Gutachten(
                    id=str(uuid.uuid4()),
                    einleitung=f"Die vorliegende Klausur von {student.student_name} im Fach {klausur.subject} "
                              f"wird im Folgenden nach den Kriterien des niedersächsischen Kerncurriculums bewertet.",
                    hauptteil=f"Die Arbeit zeigt in der inhaltlichen Auseinandersetzung mit dem Thema "
                             f"{'überzeugende' if student.grade_points >= 10 else 'grundlegende'} Kompetenzen. "
                             f"Die sprachliche Gestaltung ist {'angemessen' if student.grade_points >= 8 else 'ausbaufähig'}. "
                             f"Der Aufbau der Argumentation folgt {'einer klaren' if student.grade_points >= 10 else 'einer erkennbaren'} Struktur.",
                    fazit=f"Insgesamt erreicht die Arbeit {student.raw_points} von "
                         f"{klausur.erwartungshorizont.max_points if klausur.erwartungshorizont else 100} Punkten, "
                         f"was der Note {grade_label} entspricht.",
                    staerken=["Textverständnis", "Argumentationsaufbau"] if student.grade_points >= 10 else ["Grundlegende Texterfassung"],
                    schwaechen=["Sprachliche Vielfalt"] if student.grade_points >= 10 else ["Inhaltliche Tiefe", "Sprachliche Richtigkeit"],
                    generated=True,
                    edited=False,
                    created_at=now
                )

                student.gutachten = gutachten
                student.updated_at = now

                logger.info(f"Generated gutachten for student {student_id}")
                return _to_gutachten_response(gutachten)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.put("/students/{student_id}/gutachten", response_model=GutachtenResponse)
async def update_gutachten(student_id: str, data: GutachtenCreate):
    """Aktualisiert das Gutachten."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                now = datetime.utcnow()

                if student.gutachten:
                    student.gutachten.einleitung = data.einleitung
                    student.gutachten.hauptteil = data.hauptteil
                    student.gutachten.fazit = data.fazit
                    student.gutachten.staerken = data.staerken
                    student.gutachten.schwaechen = data.schwaechen
                    student.gutachten.edited = True
                else:
                    student.gutachten = Gutachten(
                        id=str(uuid.uuid4()),
                        einleitung=data.einleitung,
                        hauptteil=data.hauptteil,
                        fazit=data.fazit,
                        staerken=data.staerken,
                        schwaechen=data.schwaechen,
                        generated=False,
                        edited=True,
                        created_at=now
                    )

                student.updated_at = now
                return _to_gutachten_response(student.gutachten)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


# ============================================================================
# API Endpoints - Note & Finalisierung
# ============================================================================

@router.post("/students/{student_id}/grade", response_model=StudentKlausurResponse)
async def calculate_grade(student_id: str):
    """Berechnet die Note neu."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                max_points = klausur.erwartungshorizont.max_points if klausur.erwartungshorizont else 100
                student.raw_points = calculate_raw_points(student.criteria_scores, max_points)

                percentage = (student.raw_points / max_points * 100) if max_points > 0 else 0
                student.grade_points = calculate_15_point_grade(percentage)

                student.updated_at = datetime.utcnow()
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.put("/students/{student_id}/finalize", response_model=StudentKlausurResponse)
async def finalize_student(student_id: str):
    """Schließt eine Schülerarbeit ab."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                student.status = StudentKlausurStatus.COMPLETED
                student.updated_at = datetime.utcnow()

                # Prüfe ob alle Schüler abgeschlossen sind
                all_completed = all(
                    s.status == StudentKlausurStatus.COMPLETED
                    for s in klausur.students
                )
                if all_completed and klausur.students:
                    klausur.status = KlausurStatus.COMPLETED
                    klausur.updated_at = datetime.utcnow()

                logger.info(f"Finalized student {student_id}")
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


# ============================================================================
# API Endpoints - Erst-/Zweitprüfer
# ============================================================================

@router.post("/students/{student_id}/examiner", response_model=StudentKlausurResponse)
async def submit_examiner_result(student_id: str, data: ExaminerResultCreate):
    """Reicht Prüfer-Ergebnis ein."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                now = datetime.utcnow()

                result = ExaminerResult(
                    examiner_id=data.examiner_id,
                    examiner_name=data.examiner_name,
                    grade_points=data.grade_points,
                    comment=data.comment,
                    submitted_at=now
                )

                if student.status == StudentKlausurStatus.FIRST_EXAMINER:
                    student.first_examiner = result
                    student.status = StudentKlausurStatus.SECOND_EXAMINER
                elif student.status == StudentKlausurStatus.SECOND_EXAMINER:
                    student.second_examiner = result

                    # Berechne Durchschnitt wenn beide Prüfer bewertet haben
                    if student.first_examiner:
                        avg = (student.first_examiner.grade_points + result.grade_points) / 2
                        student.grade_points = round(avg)

                    student.status = StudentKlausurStatus.COMPLETED
                else:
                    raise HTTPException(
                        status_code=400,
                        detail=f"Kann Prüfer-Ergebnis nicht einreichen. Status: {student.status}"
                    )

                student.updated_at = now
                return _to_student_klausur_response(student)

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


@router.get("/examiner/queue", response_model=List[StudentKlausurResponse])
async def get_examiner_queue(examiner_role: str = "first"):
    """Gibt Warteschlange für Prüfer zurück."""
    target_status = (
        StudentKlausurStatus.FIRST_EXAMINER
        if examiner_role == "first"
        else StudentKlausurStatus.SECOND_EXAMINER
    )

    queue = []
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.status == target_status:
                queue.append(_to_student_klausur_response(student))

    return queue


# ============================================================================
# API Endpoints - Fairness & Export
# ============================================================================

@router.get("/klausuren/{klausur_id}/fairness", response_model=FairnessReport)
async def get_fairness_report(klausur_id: str):
    """Erstellt Fairness-Bericht für Klausur."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    students = klausur.students
    if not students:
        raise HTTPException(status_code=400, detail="Keine Schülerarbeiten vorhanden")

    # Notenverteilung
    grade_distribution = {}
    for s in students:
        grade_distribution[s.grade_points] = grade_distribution.get(s.grade_points, 0) + 1

    # Durchschnitte
    avg_grade = sum(s.grade_points for s in students) / len(students)

    criteria_averages = {}
    for criterion in DEFAULT_CRITERIA:
        scores = [s.criteria_scores[criterion].score for s in students if criterion in s.criteria_scores]
        if scores:
            criteria_averages[criterion] = sum(scores) / len(scores)

    # Ausreißer (>2 Standardabweichungen)
    grades = [s.grade_points for s in students]
    mean = sum(grades) / len(grades)
    variance = sum((g - mean) ** 2 for g in grades) / len(grades)
    std_dev = variance ** 0.5

    outliers = []
    for s in students:
        if abs(s.grade_points - mean) > 2 * std_dev:
            outliers.append({
                "student_name": s.student_name,
                "grade_points": s.grade_points,
                "deviation": s.grade_points - mean
            })

    # Empfehlungen
    recommendations = []
    if std_dev > 4:
        recommendations.append("Hohe Streuung der Noten - Erwartungshorizont prüfen")
    if avg_grade < 5:
        recommendations.append("Durchschnitt unter 5 Punkten - Aufgabenstellung überprüfen")
    if avg_grade > 12:
        recommendations.append("Durchschnitt über 12 Punkten - Bewertungsmaßstab prüfen")
    if outliers:
        recommendations.append(f"{len(outliers)} Ausreißer gefunden - Einzelfälle prüfen")

    return FairnessReport(
        klausur_id=klausur_id,
        total_students=len(students),
        average_grade=avg_grade,
        grade_distribution=grade_distribution,
        criteria_averages=criteria_averages,
        outliers=outliers,
        recommendations=recommendations
    )


@router.post("/klausuren/{klausur_id}/export")
async def export_klausur(
    klausur_id: str,
    student_ids: Optional[List[str]] = None,
    include_gutachten: bool = True
):
    """Exportiert Klausur als PDF."""
    klausur = _klausuren.get(klausur_id)
    if not klausur:
        raise HTTPException(status_code=404, detail="Klausur nicht gefunden")

    # Filter Schüler falls IDs angegeben
    students = klausur.students
    if student_ids:
        students = [s for s in students if s.id in student_ids]

    if not students:
        raise HTTPException(status_code=400, detail="Keine Schüler zum Exportieren")

    # Demo-Response (in Produktion: PDF generieren)
    return {
        "status": "success",
        "klausur_id": klausur_id,
        "exported_students": len(students),
        "message": "PDF-Export wird generiert..."
    }


@router.get("/students/{student_id}/export-pdf")
async def export_student_pdf(student_id: str):
    """Exportiert einzelne Schülerarbeit als PDF."""
    for klausur in _klausuren.values():
        for student in klausur.students:
            if student.id == student_id:
                try:
                    pdf_service = PDFService()

                    # Erstelle HTML für Gutachten
                    grade_label = get_grade_label(student.grade_points)

                    html_content = f"""
                    <html>
                    <head>
                        <style>
                            body {{ font-family: Arial, sans-serif; margin: 40px; }}
                            h1 {{ color: #333; }}
                            h2 {{ color: #666; border-bottom: 1px solid #ccc; padding-bottom: 5px; }}
                            .info {{ margin-bottom: 20px; }}
                            .info p {{ margin: 5px 0; }}
                            .grade {{ font-size: 24px; font-weight: bold; color: #2563eb; }}
                            .gutachten {{ margin-top: 30px; }}
                            .staerken {{ color: #059669; }}
                            .schwaechen {{ color: #dc2626; }}
                            ul {{ margin: 10px 0; }}
                        </style>
                    </head>
                    <body>
                        <h1>{klausur.title}</h1>
                        <div class="info">
                            <p><strong>Schüler/in:</strong> {student.student_name}</p>
                            <p><strong>Fach:</strong> {klausur.subject}</p>
                            <p><strong>Kurs:</strong> {klausur.kurs}</p>
                            <p><strong>Datum:</strong> {student.created_at.strftime('%d.%m.%Y')}</p>
                        </div>

                        <h2>Ergebnis</h2>
                        <p class="grade">{student.grade_points} Punkte - {grade_label}</p>
                        <p>Erreichte Rohpunkte: {student.raw_points} / {klausur.erwartungshorizont.max_points if klausur.erwartungshorizont else 100}</p>

                        <h2>Kriterien-Bewertung</h2>
                        <table border="1" cellpadding="8" cellspacing="0" style="border-collapse: collapse; width: 100%;">
                            <tr style="background: #f3f4f6;">
                                <th>Kriterium</th>
                                <th>Gewichtung</th>
                                <th>Erreicht</th>
                            </tr>
                    """

                    for crit_name, crit_data in student.criteria_scores.items():
                        label = DEFAULT_CRITERIA.get(crit_name, {}).get("label", crit_name)
                        html_content += f"""
                            <tr>
                                <td>{label}</td>
                                <td>{int(crit_data.weight * 100)}%</td>
                                <td>{crit_data.score}%</td>
                            </tr>
                        """

                    html_content += "</table>"

                    if student.gutachten:
                        html_content += f"""
                        <div class="gutachten">
                            <h2>Gutachten</h2>
                            <p>{student.gutachten.einleitung}</p>
                            <p>{student.gutachten.hauptteil}</p>
                            <p>{student.gutachten.fazit}</p>

                            <h3 class="staerken">Stärken</h3>
                            <ul>
                                {''.join(f'<li>{s}</li>' for s in student.gutachten.staerken)}
                            </ul>

                            <h3 class="schwaechen">Verbesserungsbereiche</h3>
                            <ul>
                                {''.join(f'<li>{s}</li>' for s in student.gutachten.schwaechen)}
                            </ul>
                        </div>
                        """

                    html_content += """
                        <div style="margin-top: 50px; border-top: 1px solid #ccc; padding-top: 20px;">
                            <p>Datum: _________________</p>
                            <p>Unterschrift Lehrkraft: _________________</p>
                        </div>
                    </body>
                    </html>
                    """

                    pdf_bytes = pdf_service.html_to_pdf(html_content)

                    return Response(
                        content=pdf_bytes,
                        media_type="application/pdf",
                        headers={
                            "Content-Disposition": f'attachment; filename="klausur_{student.student_name}_{klausur.subject}.pdf"'
                        }
                    )

                except Exception as e:
                    logger.error(f"PDF export error: {e}")
                    raise HTTPException(status_code=500, detail=f"PDF-Export fehlgeschlagen: {str(e)}")

    raise HTTPException(status_code=404, detail="Schülerarbeit nicht gefunden")


# ============================================================================
# API Endpoints - Hilfsfunktionen
# ============================================================================

@router.get("/grade-scale", response_model=Dict[str, Any])
async def get_grade_scale():
    """Gibt den 15-Punkte-Notenschlüssel zurück."""
    return {
        "thresholds": GRADE_THRESHOLDS,
        "labels": GRADE_LABELS
    }


@router.get("/criteria", response_model=Dict[str, Dict[str, Any]])
async def get_default_criteria():
    """Gibt die Standard-Bewertungskriterien zurück."""
    return DEFAULT_CRITERIA


# ============================================================================
# Backwards-compatibility aliases (used by tests)
# ============================================================================
klausuren_db = _klausuren
