"""
Unit Tests for Teacher Dashboard API
Tests for unit assignment and class analytics endpoints
"""
import pytest
from unittest.mock import patch, AsyncMock, MagicMock
from fastapi.testclient import TestClient
from datetime import datetime, timedelta
import uuid
import sys

sys.path.insert(0, '..')

from teacher_dashboard_api import (
    router,
    AssignUnitRequest,
    TeacherControlSettings,
    UnitAssignmentStatus,
)
from fastapi import FastAPI

# Create test app
app = FastAPI()
app.include_router(router)
client = TestClient(app)


class TestTeacherAuth:
    """Test teacher authentication"""

    def test_dashboard_without_auth_dev_mode(self):
        """Test dashboard access in dev mode (no auth required)"""
        response = client.get("/api/teacher/dashboard")
        # In dev mode, should return demo teacher
        assert response.status_code == 200
        data = response.json()
        assert "teacher" in data
        assert "classes" in data

    def test_assignments_without_auth_dev_mode(self):
        """Test assignments list in dev mode"""
        response = client.get("/api/teacher/assignments")
        assert response.status_code == 200
        assert isinstance(response.json(), list)


class TestUnitAssignments:
    """Test unit assignment endpoints"""

    def test_create_assignment(self):
        """Test creating a new unit assignment"""
        request_data = {
            "unit_id": "demo_unit_v1",
            "class_id": "class-5a",
            "settings": {
                "allow_skip": True,
                "allow_replay": True,
                "max_time_per_stop_sec": 90,
                "show_hints": True,
                "require_precheck": True,
                "require_postcheck": True
            }
        }

        response = client.post("/api/teacher/assignments", json=request_data)

        assert response.status_code == 200
        data = response.json()
        assert "assignment_id" in data
        assert data["unit_id"] == "demo_unit_v1"
        assert data["class_id"] == "class-5a"
        assert data["status"] == "active"
        assert "settings" in data

    def test_create_assignment_with_due_date(self):
        """Test creating assignment with due date"""
        due_date = (datetime.utcnow() + timedelta(days=7)).isoformat()
        request_data = {
            "unit_id": "demo_unit_v1",
            "class_id": "class-6b",
            "due_date": due_date,
            "notes": "Bitte bis naechste Woche fertig"
        }

        response = client.post("/api/teacher/assignments", json=request_data)

        assert response.status_code == 200
        data = response.json()
        assert data["notes"] == "Bitte bis naechste Woche fertig"

    def test_create_assignment_minimal(self):
        """Test creating assignment with minimal data"""
        request_data = {
            "unit_id": "demo_unit_v1",
            "class_id": "class-7a"
        }

        response = client.post("/api/teacher/assignments", json=request_data)

        assert response.status_code == 200
        data = response.json()
        # Default settings should be applied
        assert data["settings"]["allow_skip"] == True
        assert data["settings"]["allow_replay"] == True

    def test_list_assignments(self):
        """Test listing all assignments"""
        # Create some assignments first
        for i in range(3):
            client.post("/api/teacher/assignments", json={
                "unit_id": f"unit_{i}",
                "class_id": f"class_{i}"
            })

        response = client.get("/api/teacher/assignments")

        assert response.status_code == 200
        data = response.json()
        assert isinstance(data, list)
        assert len(data) >= 3

    def test_list_assignments_filter_by_class(self):
        """Test listing assignments filtered by class"""
        # Create assignment for specific class
        client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "filter-test-class"
        })

        response = client.get("/api/teacher/assignments?class_id=filter-test-class")

        assert response.status_code == 200
        data = response.json()
        assert isinstance(data, list)
        # All returned should be for this class
        for assignment in data:
            assert assignment["class_id"] == "filter-test-class"

    def test_list_assignments_filter_by_status(self):
        """Test listing assignments filtered by status"""
        response = client.get("/api/teacher/assignments?status=active")

        assert response.status_code == 200
        data = response.json()
        for assignment in data:
            assert assignment["status"] == "active"

    def test_get_single_assignment(self):
        """Test getting a single assignment"""
        # Create assignment
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "get-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        # Get it
        response = client.get(f"/api/teacher/assignments/{assignment_id}")

        assert response.status_code == 200
        data = response.json()
        assert data["assignment_id"] == assignment_id
        assert data["unit_id"] == "demo_unit_v1"

    def test_get_nonexistent_assignment(self):
        """Test getting non-existent assignment"""
        response = client.get(f"/api/teacher/assignments/{uuid.uuid4()}")
        assert response.status_code == 404

    def test_update_assignment_settings(self):
        """Test updating assignment settings"""
        # Create assignment
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "update-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        # Update settings
        update_response = client.put(
            f"/api/teacher/assignments/{assignment_id}",
            params={
                "allow_skip": False,
                "allow_replay": False
            }
        )

        # Note: Current implementation uses query params, might need adjustment
        # This test documents expected behavior

    def test_delete_assignment(self):
        """Test deleting an assignment"""
        # Create assignment
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "delete-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        # Delete it
        response = client.delete(f"/api/teacher/assignments/{assignment_id}")

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

        # Verify it's gone
        get_response = client.get(f"/api/teacher/assignments/{assignment_id}")
        assert get_response.status_code == 404


class TestAssignmentProgress:
    """Test assignment progress endpoints"""

    def test_get_assignment_progress(self):
        """Test getting progress for an assignment"""
        # Create assignment
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "progress-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        # Get progress
        response = client.get(f"/api/teacher/assignments/{assignment_id}/progress")

        assert response.status_code == 200
        data = response.json()
        assert "assignment_id" in data
        assert "unit_id" in data
        assert "total_students" in data
        assert "started_count" in data
        assert "completed_count" in data
        assert "avg_completion_rate" in data
        assert "students" in data

    def test_progress_contains_student_details(self):
        """Test that progress contains student details"""
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "progress-details-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        response = client.get(f"/api/teacher/assignments/{assignment_id}/progress")

        assert response.status_code == 200
        data = response.json()
        assert isinstance(data["students"], list)
        # Each student should have these fields
        for student in data["students"]:
            assert "student_id" in student
            assert "student_name" in student
            assert "status" in student
            assert "completion_rate" in student


class TestClassAnalytics:
    """Test class analytics endpoints"""

    def test_get_class_analytics(self):
        """Test getting analytics for a class"""
        response = client.get("/api/teacher/classes/test-class-123/analytics")

        assert response.status_code == 200
        data = response.json()
        assert "class_id" in data
        assert "total_units_assigned" in data
        assert "units_completed" in data
        assert "avg_completion_rate" in data
        assert "common_misconceptions" in data

    def test_get_student_progress(self):
        """Test getting progress for a specific student"""
        response = client.get(f"/api/teacher/students/{uuid.uuid4()}/progress")

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


class TestContentResources:
    """Test content resource endpoints"""

    def test_get_assignment_resources(self):
        """Test getting resources for an assignment"""
        # Create assignment
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "resources-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        # Get resources
        response = client.get(f"/api/teacher/assignments/{assignment_id}/resources")

        assert response.status_code == 200
        data = response.json()
        assert isinstance(data, list)
        assert len(data) >= 1

        # Check resource structure
        for resource in data:
            assert "resource_type" in resource
            assert "title" in resource
            assert "url" in resource
            assert "unit_id" in resource

    def test_resources_include_h5p_and_pdf(self):
        """Test that resources include both H5P and PDF"""
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "resource-types-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        response = client.get(f"/api/teacher/assignments/{assignment_id}/resources")

        data = response.json()
        resource_types = [r["resource_type"] for r in data]

        assert "h5p" in resource_types
        assert "pdf" in resource_types or "worksheet" in resource_types

    def test_regenerate_content(self):
        """Test triggering content regeneration"""
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "regenerate-test-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        response = client.post(
            f"/api/teacher/assignments/{assignment_id}/regenerate-content?resource_type=all"
        )

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


class TestAvailableUnits:
    """Test available units endpoints"""

    def test_list_available_units(self):
        """Test listing available units"""
        response = client.get("/api/teacher/units/available")

        assert response.status_code == 200
        data = response.json()
        assert isinstance(data, list)
        assert len(data) >= 1

    def test_available_units_structure(self):
        """Test structure of available units"""
        response = client.get("/api/teacher/units/available")

        data = response.json()
        for unit in data:
            assert "unit_id" in unit
            assert "template" in unit
            assert "grade_band" in unit
            assert "duration_minutes" in unit

    def test_filter_by_grade(self):
        """Test filtering available units by grade"""
        response = client.get("/api/teacher/units/available?grade=5")

        assert response.status_code == 200
        # Should return units appropriate for grade 5

    def test_filter_by_template(self):
        """Test filtering available units by template"""
        response = client.get("/api/teacher/units/available?template=flight_path")

        assert response.status_code == 200
        data = response.json()
        # When database is available, filter should work
        # When using fallback data, filter may not be applied server-side
        # At minimum, verify response structure is correct
        assert isinstance(data, list)
        for unit in data:
            assert "template" in unit


class TestDashboard:
    """Test dashboard overview endpoint"""

    def test_get_dashboard(self):
        """Test getting dashboard overview"""
        response = client.get("/api/teacher/dashboard")

        assert response.status_code == 200
        data = response.json()
        assert "teacher" in data
        assert "classes" in data
        assert "active_assignments" in data
        assert "alerts" in data

    def test_dashboard_teacher_info(self):
        """Test dashboard contains teacher info"""
        response = client.get("/api/teacher/dashboard")

        data = response.json()
        teacher = data["teacher"]
        assert "id" in teacher
        assert "name" in teacher


class TestHealthEndpoint:
    """Test health endpoint"""

    def test_health_check(self):
        """Test health check endpoint"""
        response = client.get("/api/teacher/health")

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "healthy"
        assert data["service"] == "teacher-dashboard"


class TestPydanticModels:
    """Test Pydantic model validation"""

    def test_assign_unit_request_validation(self):
        """Test AssignUnitRequest validation"""
        request = AssignUnitRequest(
            unit_id="test_unit",
            class_id="test_class"
        )
        assert request.unit_id == "test_unit"
        assert request.class_id == "test_class"
        assert request.settings is None

    def test_teacher_control_settings_defaults(self):
        """Test TeacherControlSettings default values"""
        settings = TeacherControlSettings()

        assert settings.allow_skip == True
        assert settings.allow_replay == True
        assert settings.max_time_per_stop_sec == 90
        assert settings.show_hints == True
        assert settings.require_precheck == True
        assert settings.require_postcheck == True

    def test_teacher_control_settings_custom(self):
        """Test TeacherControlSettings with custom values"""
        settings = TeacherControlSettings(
            allow_skip=False,
            allow_replay=False,
            max_time_per_stop_sec=120,
            show_hints=False,
            require_precheck=False,
            require_postcheck=True
        )

        assert settings.allow_skip == False
        assert settings.allow_replay == False
        assert settings.max_time_per_stop_sec == 120

    def test_unit_assignment_status_enum(self):
        """Test UnitAssignmentStatus enum values"""
        assert UnitAssignmentStatus.DRAFT == "draft"
        assert UnitAssignmentStatus.ACTIVE == "active"
        assert UnitAssignmentStatus.COMPLETED == "completed"
        assert UnitAssignmentStatus.ARCHIVED == "archived"


class TestEdgeCases:
    """Test edge cases and error handling"""

    def test_create_assignment_same_unit_class_twice(self):
        """Test creating same assignment twice"""
        request_data = {
            "unit_id": "duplicate_test_unit",
            "class_id": "duplicate_test_class"
        }

        response1 = client.post("/api/teacher/assignments", json=request_data)
        response2 = client.post("/api/teacher/assignments", json=request_data)

        # Both should succeed (different assignment IDs)
        assert response1.status_code == 200
        assert response2.status_code == 200
        assert response1.json()["assignment_id"] != response2.json()["assignment_id"]

    def test_progress_for_empty_class(self):
        """Test getting progress for class with no students"""
        create_response = client.post("/api/teacher/assignments", json={
            "unit_id": "demo_unit_v1",
            "class_id": "empty-class"
        })
        assignment_id = create_response.json()["assignment_id"]

        response = client.get(f"/api/teacher/assignments/{assignment_id}/progress")

        assert response.status_code == 200
        data = response.json()
        assert data["total_students"] >= 0
        assert isinstance(data["students"], list)

    def test_analytics_for_class_with_no_assignments(self):
        """Test analytics for class with no assignments"""
        response = client.get("/api/teacher/classes/nonexistent-class/analytics")

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