"""
Tests für Correction API.

Testet den Korrektur-Workflow:
- Upload
- OCR
- Analyse
- Export
"""

import pytest
import io
from unittest.mock import patch, MagicMock
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

client = TestClient(app)


class TestCorrectionCreate:
    """Tests für Korrektur-Erstellung."""

    def test_create_correction_success(self):
        """Testet erfolgreiche Erstellung."""
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "student-001",
                "student_name": "Max Mustermann",
                "class_name": "7a",
                "exam_title": "Mathematik Test 1",
                "subject": "Mathematik",
                "max_points": 50.0
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert data["correction"]["student_name"] == "Max Mustermann"
        assert data["correction"]["status"] == "uploaded"
        assert data["correction"]["max_points"] == 50.0

    def test_create_correction_default_points(self):
        """Testet Erstellung mit Standard-Punktzahl."""
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "student-002",
                "student_name": "Anna Schmidt",
                "class_name": "7a",
                "exam_title": "Deutsch Aufsatz",
                "subject": "Deutsch"
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["correction"]["max_points"] == 100.0


class TestCorrectionUpload:
    """Tests für Datei-Upload."""

    def _create_correction(self):
        """Hilfsmethode zum Erstellen einer Korrektur."""
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "student-test",
                "student_name": "Test Student",
                "class_name": "Test",
                "exam_title": "Test Exam",
                "subject": "Test"
            }
        )
        return response.json()["correction"]["id"]

    def test_upload_pdf_success(self):
        """Testet PDF-Upload."""
        correction_id = self._create_correction()

        # Erstelle Mock-PDF
        pdf_content = b"%PDF-1.4 test content"
        files = {"file": ("test.pdf", io.BytesIO(pdf_content), "application/pdf")}

        with patch("correction_api._process_ocr"):
            response = client.post(
                f"/api/corrections/{correction_id}/upload",
                files=files
            )

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

    def test_upload_image_success(self):
        """Testet Bild-Upload."""
        correction_id = self._create_correction()

        # Erstelle Mock-PNG
        png_content = b"\x89PNG\r\n\x1a\n test content"
        files = {"file": ("test.png", io.BytesIO(png_content), "image/png")}

        with patch("correction_api._process_ocr"):
            response = client.post(
                f"/api/corrections/{correction_id}/upload",
                files=files
            )

        assert response.status_code == 200

    def test_upload_invalid_format(self):
        """Testet Ablehnung ungültiger Formate."""
        correction_id = self._create_correction()

        files = {"file": ("test.txt", io.BytesIO(b"text"), "text/plain")}

        response = client.post(
            f"/api/corrections/{correction_id}/upload",
            files=files
        )

        assert response.status_code == 400
        assert "Ungültiges Dateiformat" in response.json()["detail"]

    def test_upload_not_found(self):
        """Testet Upload für nicht existierende Korrektur."""
        files = {"file": ("test.pdf", io.BytesIO(b"content"), "application/pdf")}

        response = client.post(
            "/api/corrections/nonexistent/upload",
            files=files
        )

        assert response.status_code == 404


class TestCorrectionRetrieval:
    """Tests für Korrektur-Abruf."""

    def test_get_correction(self):
        """Testet Abrufen einer Korrektur."""
        # Erstelle Korrektur
        create_response = client.post(
            "/api/corrections/",
            json={
                "student_id": "get-test",
                "student_name": "Get Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = create_response.json()["correction"]["id"]

        # Rufe ab
        response = client.get(f"/api/corrections/{correction_id}")

        assert response.status_code == 200
        data = response.json()
        assert data["correction"]["id"] == correction_id

    def test_get_correction_not_found(self):
        """Testet Fehler bei nicht vorhandener Korrektur."""
        response = client.get("/api/corrections/nonexistent")
        assert response.status_code == 404

    def test_list_corrections(self):
        """Testet Auflisten von Korrekturen."""
        # Erstelle einige Korrekturen
        for i in range(3):
            client.post(
                "/api/corrections/",
                json={
                    "student_id": f"list-{i}",
                    "student_name": f"Student {i}",
                    "class_name": "ListTest",
                    "exam_title": "Test",
                    "subject": "Test"
                }
            )

        response = client.get("/api/corrections/?class_name=ListTest")

        assert response.status_code == 200
        data = response.json()
        assert data["total"] >= 3

    def test_list_corrections_filter_status(self):
        """Testet Filterung nach Status."""
        response = client.get("/api/corrections/?status=completed")

        assert response.status_code == 200
        data = response.json()
        # Alle zurückgegebenen Korrekturen sollten "completed" Status haben
        for c in data["corrections"]:
            if c.get("status"):  # Falls vorhanden
                assert c["status"] == "completed"


class TestCorrectionAnalysis:
    """Tests für Korrektur-Analyse."""

    def _create_correction_with_text(self):
        """Erstellt Korrektur mit OCR-Text."""
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "analyze-test",
                "student_name": "Analyze Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Mathematik",
                "max_points": 100.0
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Simuliere OCR-Ergebnis durch direktes Setzen
        from correction_api import _corrections, CorrectionStatus
        correction = _corrections[correction_id]
        correction.extracted_text = """
        Aufgabe 1: Die Antwort ist 42.

        Aufgabe 2: Hier ist meine Lösung für die Gleichung.

        Aufgabe 3: Das Ergebnis beträgt 15.
        """
        correction.status = CorrectionStatus.OCR_COMPLETE
        _corrections[correction_id] = correction

        return correction_id

    def test_analyze_correction(self):
        """Testet Analyse einer Korrektur."""
        correction_id = self._create_correction_with_text()

        response = client.post(
            f"/api/corrections/{correction_id}/analyze"
        )

        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert len(data["evaluations"]) > 0
        assert "suggested_grade" in data
        assert "ai_feedback" in data

    def test_analyze_with_expected_answers(self):
        """Testet Analyse mit Musterlösung."""
        correction_id = self._create_correction_with_text()

        expected = {
            "1": "42",
            "2": "Gleichung",
            "3": "15"
        }

        response = client.post(
            f"/api/corrections/{correction_id}/analyze",
            json=expected
        )

        assert response.status_code == 200
        data = response.json()
        # Mit passender Musterlösung sollten einige Antworten korrekt sein
        correct_count = sum(1 for e in data["evaluations"] if e["is_correct"])
        assert correct_count > 0

    def test_analyze_wrong_status(self):
        """Testet Analyse bei falschem Status."""
        # Neue Korrektur ohne OCR
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "wrong-status",
                "student_name": "Wrong Status",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Analyse ohne vorherige OCR
        response = client.post(f"/api/corrections/{correction_id}/analyze")

        assert response.status_code == 400


class TestCorrectionUpdate:
    """Tests für Korrektur-Aktualisierung."""

    def test_update_correction(self):
        """Testet Aktualisierung einer Korrektur."""
        # Erstelle Korrektur
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "update-test",
                "student_name": "Update Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Aktualisiere
        response = client.put(
            f"/api/corrections/{correction_id}",
            json={
                "grade": "2",
                "total_points": 85.0,
                "teacher_notes": "Gute Arbeit!"
            }
        )

        assert response.status_code == 200
        data = response.json()
        assert data["correction"]["grade"] == "2"
        assert data["correction"]["total_points"] == 85.0
        assert data["correction"]["teacher_notes"] == "Gute Arbeit!"

    def test_complete_correction(self):
        """Testet Abschluss einer Korrektur."""
        # Erstelle Korrektur
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "complete-test",
                "student_name": "Complete Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Schließe ab
        response = client.post(f"/api/corrections/{correction_id}/complete")

        assert response.status_code == 200
        data = response.json()
        assert data["correction"]["status"] == "completed"


class TestCorrectionDelete:
    """Tests für Korrektur-Löschung."""

    def test_delete_correction(self):
        """Testet Löschen einer Korrektur."""
        # Erstelle Korrektur
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "delete-test",
                "student_name": "Delete Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Lösche
        response = client.delete(f"/api/corrections/{correction_id}")

        assert response.status_code == 200
        assert response.json()["status"] == "deleted"

        # Prüfe dass gelöscht
        response = client.get(f"/api/corrections/{correction_id}")
        assert response.status_code == 404

    def test_delete_not_found(self):
        """Testet Fehler beim Löschen nicht existierender Korrektur."""
        response = client.delete("/api/corrections/nonexistent")
        assert response.status_code == 404


class TestClassSummary:
    """Tests für Klassen-Zusammenfassung."""

    def _create_completed_corrections(self, class_name: str, count: int):
        """Erstellt abgeschlossene Korrekturen für eine Klasse."""
        from correction_api import _corrections, CorrectionStatus
        import uuid
        from datetime import datetime

        for i in range(count):
            response = client.post(
                "/api/corrections/",
                json={
                    "student_id": f"summary-{i}",
                    "student_name": f"Student {i}",
                    "class_name": class_name,
                    "exam_title": "Summary Test",
                    "subject": "Test",
                    "max_points": 100.0
                }
            )
            correction_id = response.json()["correction"]["id"]

            # Setze als completed mit Punkten
            correction = _corrections[correction_id]
            correction.status = CorrectionStatus.COMPLETED
            correction.total_points = 70 + i * 5  # 70, 75, 80, ...
            correction.percentage = correction.total_points
            correction.grade = str(3 - i // 2)  # Verschiedene Noten
            _corrections[correction_id] = correction

    def test_class_summary(self):
        """Testet Klassen-Zusammenfassung."""
        class_name = "SummaryTestClass"
        self._create_completed_corrections(class_name, 3)

        response = client.get(f"/api/corrections/class/{class_name}/summary")

        assert response.status_code == 200
        data = response.json()
        assert data["class_name"] == class_name
        assert data["total_students"] == 3
        assert "average_percentage" in data
        assert "grade_distribution" in data
        assert len(data["corrections"]) == 3

    def test_class_summary_empty(self):
        """Testet Zusammenfassung für leere Klasse."""
        response = client.get("/api/corrections/class/EmptyClass/summary")

        assert response.status_code == 200
        data = response.json()
        assert data["total_students"] == 0
        assert data["average_percentage"] == 0


class TestGradeCalculation:
    """Tests für Notenberechnung."""

    def test_grade_1(self):
        """Testet Note 1 (>=92%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(92) == "1"
        assert _calculate_grade(100) == "1"

    def test_grade_2(self):
        """Testet Note 2 (81-91%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(81) == "2"
        assert _calculate_grade(91) == "2"

    def test_grade_3(self):
        """Testet Note 3 (67-80%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(67) == "3"
        assert _calculate_grade(80) == "3"

    def test_grade_4(self):
        """Testet Note 4 (50-66%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(50) == "4"
        assert _calculate_grade(66) == "4"

    def test_grade_5(self):
        """Testet Note 5 (30-49%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(30) == "5"
        assert _calculate_grade(49) == "5"

    def test_grade_6(self):
        """Testet Note 6 (<30%)."""
        from correction_api import _calculate_grade
        assert _calculate_grade(29) == "6"
        assert _calculate_grade(0) == "6"


class TestOCRRetry:
    """Tests für OCR-Wiederholung."""

    def test_retry_ocr(self):
        """Testet OCR-Wiederholung."""
        # Erstelle Korrektur mit Datei
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "retry-test",
                "student_name": "Retry Test",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        # Setze file_path manuell (simuliert Upload)
        from correction_api import _corrections
        import tempfile
        import os

        # Erstelle temp file
        fd, path = tempfile.mkstemp(suffix=".pdf")
        os.write(fd, b"%PDF-1.4 test")
        os.close(fd)

        correction = _corrections[correction_id]
        correction.file_path = path
        _corrections[correction_id] = correction

        # Retry OCR
        with patch("correction_api._process_ocr"):
            response = client.post(f"/api/corrections/{correction_id}/ocr/retry")

        assert response.status_code == 200

        # Cleanup
        os.remove(path)

    def test_retry_ocr_no_file(self):
        """Testet Fehler bei OCR-Retry ohne Datei."""
        response = client.post(
            "/api/corrections/",
            json={
                "student_id": "retry-no-file",
                "student_name": "No File",
                "class_name": "Test",
                "exam_title": "Test",
                "subject": "Test"
            }
        )
        correction_id = response.json()["correction"]["id"]

        response = client.post(f"/api/corrections/{correction_id}/ocr/retry")

        assert response.status_code == 400
        assert "Keine Datei" in response.json()["detail"]
