"""
Correction API - REST API für Klassenarbeits-Korrektur.

Workflow:
1. Upload: Gescannte Klassenarbeit hochladen
2. OCR: Text aus Handschrift extrahieren
3. Analyse: Antworten analysieren und bewerten
4. Feedback: KI-generiertes Feedback erstellen
5. Export: Korrigierte Arbeit als PDF exportieren

Integriert:
- FileProcessor für OCR
- PDFService für Export
- LLM für Analyse und Feedback
"""

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 fastapi import APIRouter, HTTPException, UploadFile, File, Form, BackgroundTasks
from pydantic import BaseModel, Field

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

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

logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/corrections",
    tags=["corrections"],
)

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


# ============================================================================
# Enums and Models
# ============================================================================

class CorrectionStatus(str, Enum):
    """Status einer Korrektur."""
    UPLOADED = "uploaded"           # Datei hochgeladen
    PROCESSING = "processing"       # OCR läuft
    OCR_COMPLETE = "ocr_complete"   # OCR abgeschlossen
    ANALYZING = "analyzing"         # Analyse läuft
    ANALYZED = "analyzed"           # Analyse abgeschlossen
    REVIEWING = "reviewing"         # Lehrkraft prüft
    COMPLETED = "completed"         # Korrektur abgeschlossen
    ERROR = "error"                 # Fehler aufgetreten


class AnswerEvaluation(BaseModel):
    """Bewertung einer einzelnen Antwort."""
    question_number: int
    extracted_text: str
    points_possible: float
    points_awarded: float
    feedback: str
    is_correct: bool
    confidence: float  # 0-1, wie sicher die OCR/Analyse ist


class CorrectionCreate(BaseModel):
    """Request zum Erstellen einer neuen Korrektur."""
    student_id: str
    student_name: str
    class_name: str
    exam_title: str
    subject: str
    max_points: float = Field(default=100.0, ge=0)
    expected_answers: Optional[Dict[str, str]] = None  # Musterlösung


class CorrectionUpdate(BaseModel):
    """Request zum Aktualisieren einer Korrektur."""
    evaluations: Optional[List[AnswerEvaluation]] = None
    total_points: Optional[float] = None
    grade: Optional[str] = None
    teacher_notes: Optional[str] = None
    status: Optional[CorrectionStatus] = None


class Correction(BaseModel):
    """Eine Korrektur."""
    id: str
    student_id: str
    student_name: str
    class_name: str
    exam_title: str
    subject: str
    max_points: float
    total_points: float = 0.0
    percentage: float = 0.0
    grade: Optional[str] = None
    status: CorrectionStatus
    file_path: Optional[str] = None
    extracted_text: Optional[str] = None
    evaluations: List[AnswerEvaluation] = []
    teacher_notes: Optional[str] = None
    ai_feedback: Optional[str] = None
    created_at: datetime
    updated_at: datetime


class CorrectionResponse(BaseModel):
    """Response für eine Korrektur."""
    success: bool
    correction: Optional[Correction] = None
    error: Optional[str] = None


class OCRResponse(BaseModel):
    """Response für OCR-Ergebnis."""
    success: bool
    extracted_text: Optional[str] = None
    regions: List[Dict[str, Any]] = []
    confidence: float = 0.0
    error: Optional[str] = None


class AnalysisResponse(BaseModel):
    """Response für Analyse-Ergebnis."""
    success: bool
    evaluations: List[AnswerEvaluation] = []
    total_points: float = 0.0
    percentage: float = 0.0
    suggested_grade: Optional[str] = None
    ai_feedback: Optional[str] = None
    error: Optional[str] = None


# ============================================================================
# In-Memory Storage (später durch DB ersetzen)
# ============================================================================

_corrections: Dict[str, Correction] = {}


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

def _calculate_grade(percentage: float) -> str:
    """Berechnet Note aus Prozent (deutsches System)."""
    if percentage >= 92:
        return "1"
    elif percentage >= 81:
        return "2"
    elif percentage >= 67:
        return "3"
    elif percentage >= 50:
        return "4"
    elif percentage >= 30:
        return "5"
    else:
        return "6"


def _generate_ai_feedback(
    evaluations: List[AnswerEvaluation],
    total_points: float,
    max_points: float,
    subject: str
) -> str:
    """Generiert KI-Feedback basierend auf Bewertung."""
    # Ohne LLM: Einfaches Template-basiertes Feedback
    percentage = (total_points / max_points * 100) if max_points > 0 else 0
    correct_count = sum(1 for e in evaluations if e.is_correct)
    total_count = len(evaluations)

    if percentage >= 90:
        intro = "Hervorragende Leistung!"
    elif percentage >= 75:
        intro = "Gute Arbeit!"
    elif percentage >= 60:
        intro = "Insgesamt eine solide Leistung."
    elif percentage >= 50:
        intro = "Die Arbeit zeigt Grundkenntnisse, aber es gibt Verbesserungsbedarf."
    else:
        intro = "Es sind deutliche Wissenslücken erkennbar."

    # Finde Verbesserungsbereiche
    weak_areas = [e for e in evaluations if not e.is_correct]
    strengths = [e for e in evaluations if e.is_correct and e.confidence > 0.8]

    feedback_parts = [intro]

    if strengths:
        feedback_parts.append(
            f"Besonders gut gelöst: Aufgabe(n) {', '.join(str(s.question_number) for s in strengths[:3])}."
        )

    if weak_areas:
        feedback_parts.append(
            f"Übungsbedarf bei: Aufgabe(n) {', '.join(str(w.question_number) for w in weak_areas[:3])}."
        )

    feedback_parts.append(
        f"Ergebnis: {correct_count} von {total_count} Aufgaben korrekt ({percentage:.1f}%)."
    )

    return " ".join(feedback_parts)


async def _process_ocr(correction_id: str, file_path: str):
    """Background Task für OCR-Verarbeitung."""
    correction = _corrections.get(correction_id)
    if not correction:
        return

    try:
        correction.status = CorrectionStatus.PROCESSING
        _corrections[correction_id] = correction

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

        if result.success and result.text:
            correction.extracted_text = result.text
            correction.status = CorrectionStatus.OCR_COMPLETE
        else:
            correction.status = CorrectionStatus.ERROR

        correction.updated_at = datetime.utcnow()
        _corrections[correction_id] = correction

    except Exception as e:
        logger.error(f"OCR error for {correction_id}: {e}")
        correction.status = CorrectionStatus.ERROR
        correction.updated_at = datetime.utcnow()
        _corrections[correction_id] = correction


# ============================================================================
# API Endpoints
# ============================================================================

@router.post("/", response_model=CorrectionResponse)
async def create_correction(data: CorrectionCreate):
    """
    Erstellt eine neue Korrektur.

    Noch ohne Datei - diese wird separat hochgeladen.
    """
    correction_id = str(uuid.uuid4())
    now = datetime.utcnow()

    correction = Correction(
        id=correction_id,
        student_id=data.student_id,
        student_name=data.student_name,
        class_name=data.class_name,
        exam_title=data.exam_title,
        subject=data.subject,
        max_points=data.max_points,
        status=CorrectionStatus.UPLOADED,
        created_at=now,
        updated_at=now
    )

    _corrections[correction_id] = correction
    logger.info(f"Created correction {correction_id} for {data.student_name}")

    return CorrectionResponse(success=True, correction=correction)


@router.post("/{correction_id}/upload", response_model=CorrectionResponse)
async def upload_exam(
    correction_id: str,
    background_tasks: BackgroundTasks,
    file: UploadFile = File(...)
):
    """
    Lädt gescannte Klassenarbeit hoch und startet OCR.

    Unterstützte Formate: PDF, PNG, JPG, JPEG
    """
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur 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)}"
        )

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

    try:
        content = await file.read()
        with open(file_path, "wb") as f:
            f.write(content)

        correction.file_path = str(file_path)
        correction.updated_at = datetime.utcnow()
        _corrections[correction_id] = correction

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

        logger.info(f"Uploaded file for correction {correction_id}: {file.filename}")

        return CorrectionResponse(success=True, correction=correction)

    except Exception as e:
        logger.error(f"Upload error: {e}")
        return CorrectionResponse(success=False, error=str(e))


@router.get("/{correction_id}", response_model=CorrectionResponse)
async def get_correction(correction_id: str):
    """Ruft eine Korrektur ab."""
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    return CorrectionResponse(success=True, correction=correction)


@router.get("/", response_model=Dict[str, Any])
async def list_corrections(
    class_name: Optional[str] = None,
    status: Optional[CorrectionStatus] = None,
    limit: int = 50
):
    """Listet Korrekturen auf, optional gefiltert."""
    corrections = list(_corrections.values())

    if class_name:
        corrections = [c for c in corrections if c.class_name == class_name]

    if status:
        corrections = [c for c in corrections if c.status == status]

    # Sortiere nach Erstellungsdatum (neueste zuerst)
    corrections.sort(key=lambda x: x.created_at, reverse=True)

    return {
        "total": len(corrections),
        "corrections": [c.dict() for c in corrections[:limit]]
    }


@router.post("/{correction_id}/analyze", response_model=AnalysisResponse)
async def analyze_correction(
    correction_id: str,
    expected_answers: Optional[Dict[str, str]] = None
):
    """
    Analysiert die extrahierten Antworten.

    Optional mit Musterlösung für automatische Bewertung.
    """
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    if correction.status not in [CorrectionStatus.OCR_COMPLETE, CorrectionStatus.ANALYZED]:
        raise HTTPException(
            status_code=400,
            detail=f"Korrektur im falschen Status: {correction.status}"
        )

    if not correction.extracted_text:
        raise HTTPException(status_code=400, detail="Kein extrahierter Text vorhanden")

    try:
        correction.status = CorrectionStatus.ANALYZING
        _corrections[correction_id] = correction

        # Einfache Analyse ohne LLM
        # Teile Text in Abschnitte (simuliert Aufgabenerkennung)
        text_parts = correction.extracted_text.split('\n\n')
        evaluations = []

        for i, part in enumerate(text_parts[:10], start=1):  # Max 10 Aufgaben
            if len(part.strip()) < 5:
                continue

            # Simulierte Bewertung
            # In Produktion würde hier LLM-basierte Analyse stattfinden
            expected = expected_answers.get(str(i), "") if expected_answers else ""

            # Einfacher Textvergleich (in Produktion: semantischer Vergleich)
            is_correct = bool(expected and expected.lower() in part.lower())
            points = correction.max_points / len(text_parts) if text_parts else 0

            evaluation = AnswerEvaluation(
                question_number=i,
                extracted_text=part[:200],  # Kürzen für Response
                points_possible=points,
                points_awarded=points if is_correct else points * 0.5,  # Teilpunkte
                feedback=f"Antwort zu Aufgabe {i}" + (" korrekt." if is_correct else " mit Verbesserungsbedarf."),
                is_correct=is_correct,
                confidence=0.7  # Simulierte Confidence
            )
            evaluations.append(evaluation)

        # Berechne Gesamtergebnis
        total_points = sum(e.points_awarded for e in evaluations)
        percentage = (total_points / correction.max_points * 100) if correction.max_points > 0 else 0
        suggested_grade = _calculate_grade(percentage)

        # Generiere Feedback
        ai_feedback = _generate_ai_feedback(
            evaluations, total_points, correction.max_points, correction.subject
        )

        # Aktualisiere Korrektur
        correction.evaluations = evaluations
        correction.total_points = total_points
        correction.percentage = percentage
        correction.grade = suggested_grade
        correction.ai_feedback = ai_feedback
        correction.status = CorrectionStatus.ANALYZED
        correction.updated_at = datetime.utcnow()
        _corrections[correction_id] = correction

        logger.info(f"Analysis complete for {correction_id}: {total_points}/{correction.max_points}")

        return AnalysisResponse(
            success=True,
            evaluations=evaluations,
            total_points=total_points,
            percentage=percentage,
            suggested_grade=suggested_grade,
            ai_feedback=ai_feedback
        )

    except Exception as e:
        logger.error(f"Analysis error: {e}")
        correction.status = CorrectionStatus.ERROR
        _corrections[correction_id] = correction
        return AnalysisResponse(success=False, error=str(e))


@router.put("/{correction_id}", response_model=CorrectionResponse)
async def update_correction(correction_id: str, data: CorrectionUpdate):
    """
    Aktualisiert eine Korrektur.

    Ermöglicht manuelle Anpassungen durch die Lehrkraft.
    """
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    if data.evaluations is not None:
        correction.evaluations = data.evaluations
        correction.total_points = sum(e.points_awarded for e in data.evaluations)
        correction.percentage = (
            correction.total_points / correction.max_points * 100
        ) if correction.max_points > 0 else 0

    if data.total_points is not None:
        correction.total_points = data.total_points
        correction.percentage = (
            data.total_points / correction.max_points * 100
        ) if correction.max_points > 0 else 0

    if data.grade is not None:
        correction.grade = data.grade

    if data.teacher_notes is not None:
        correction.teacher_notes = data.teacher_notes

    if data.status is not None:
        correction.status = data.status

    correction.updated_at = datetime.utcnow()
    _corrections[correction_id] = correction

    return CorrectionResponse(success=True, correction=correction)


@router.post("/{correction_id}/complete", response_model=CorrectionResponse)
async def complete_correction(correction_id: str):
    """Markiert Korrektur als abgeschlossen."""
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    correction.status = CorrectionStatus.COMPLETED
    correction.updated_at = datetime.utcnow()
    _corrections[correction_id] = correction

    logger.info(f"Correction {correction_id} completed: {correction.grade}")

    return CorrectionResponse(success=True, correction=correction)


@router.get("/{correction_id}/export-pdf")
async def export_correction_pdf(correction_id: str):
    """
    Exportiert korrigierte Arbeit als PDF.

    Enthält:
    - Originalscan
    - Bewertungen
    - Feedback
    - Gesamtergebnis
    """
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    try:
        pdf_service = PDFService()

        # Erstelle CorrectionData
        correction_data = CorrectionData(
            student=StudentInfo(
                student_id=correction.student_id,
                name=correction.student_name,
                class_name=correction.class_name
            ),
            exam_title=correction.exam_title,
            subject=correction.subject,
            date=correction.created_at.strftime("%d.%m.%Y"),
            max_points=correction.max_points,
            achieved_points=correction.total_points,
            grade=correction.grade or "",
            percentage=correction.percentage,
            corrections=[
                {
                    "question": f"Aufgabe {e.question_number}",
                    "answer": e.extracted_text,
                    "points": f"{e.points_awarded}/{e.points_possible}",
                    "feedback": e.feedback
                }
                for e in correction.evaluations
            ],
            teacher_notes=correction.teacher_notes or "",
            ai_feedback=correction.ai_feedback or ""
        )

        # Generiere PDF
        pdf_bytes = pdf_service.generate_correction_pdf(correction_data)

        from fastapi.responses import Response

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

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


@router.delete("/{correction_id}")
async def delete_correction(correction_id: str):
    """Löscht eine Korrektur."""
    if correction_id not in _corrections:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    correction = _corrections[correction_id]

    # Lösche auch die hochgeladene Datei
    if correction.file_path and os.path.exists(correction.file_path):
        try:
            os.remove(correction.file_path)
        except Exception as e:
            logger.warning(f"Could not delete file {correction.file_path}: {e}")

    del _corrections[correction_id]
    logger.info(f"Deleted correction {correction_id}")

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


@router.get("/class/{class_name}/summary")
async def get_class_summary(class_name: str):
    """
    Gibt Zusammenfassung für eine Klasse zurück.

    Enthält Statistiken über alle Korrekturen der Klasse.
    """
    class_corrections = [
        c for c in _corrections.values()
        if c.class_name == class_name and c.status == CorrectionStatus.COMPLETED
    ]

    if not class_corrections:
        return {
            "class_name": class_name,
            "total_students": 0,
            "average_percentage": 0,
            "grade_distribution": {},
            "corrections": []
        }

    # Berechne Statistiken
    percentages = [c.percentage for c in class_corrections]
    average_percentage = sum(percentages) / len(percentages) if percentages else 0

    # Notenverteilung
    grade_distribution = {}
    for c in class_corrections:
        grade = c.grade or "?"
        grade_distribution[grade] = grade_distribution.get(grade, 0) + 1

    return {
        "class_name": class_name,
        "total_students": len(class_corrections),
        "average_percentage": round(average_percentage, 1),
        "average_points": round(
            sum(c.total_points for c in class_corrections) / len(class_corrections), 1
        ),
        "grade_distribution": grade_distribution,
        "corrections": [
            {
                "id": c.id,
                "student_name": c.student_name,
                "total_points": c.total_points,
                "percentage": c.percentage,
                "grade": c.grade
            }
            for c in sorted(class_corrections, key=lambda x: x.student_name)
        ]
    }


@router.post("/{correction_id}/ocr/retry", response_model=CorrectionResponse)
async def retry_ocr(correction_id: str, background_tasks: BackgroundTasks):
    """
    Wiederholt OCR-Verarbeitung.

    Nützlich wenn erste Verarbeitung fehlgeschlagen ist.
    """
    correction = _corrections.get(correction_id)
    if not correction:
        raise HTTPException(status_code=404, detail="Korrektur nicht gefunden")

    if not correction.file_path:
        raise HTTPException(status_code=400, detail="Keine Datei vorhanden")

    if not os.path.exists(correction.file_path):
        raise HTTPException(status_code=400, detail="Datei nicht mehr vorhanden")

    # Starte OCR erneut
    correction.status = CorrectionStatus.UPLOADED
    correction.extracted_text = None
    correction.updated_at = datetime.utcnow()
    _corrections[correction_id] = correction

    background_tasks.add_task(_process_ocr, correction_id, correction.file_path)

    return CorrectionResponse(success=True, correction=correction)
