"""Tests for QTI Pydantic types."""

import pytest
from pydantic import ValidationError

from timeback_qti import (
    AssessmentItem,
    AssessmentTest,
    CreateAssessmentItemInput,
    CreateAssessmentItemXmlInput,
    CreateAssessmentTestInput,
    CreateStimulusInput,
    DeleteResponse,
    LessonFeedback,
    ListParams,
    ListResponse,
    ProcessResponseResult,
    Stimulus,
    SubmitLessonFeedbackInput,
    UpdateAssessmentItemInput,
    UpdateAssessmentTestInput,
    UpdateStimulusInput,
    ValidateBatchInput,
    ValidateInput,
    ValidationResult,
)


class TestAssessmentItem:
    """Tests for AssessmentItem model."""

    def test_parse_minimal(self):
        """Should parse a minimal assessment item response."""
        data = {
            "identifier": "item-1",
            "title": "Test Item",
            "type": "choice",
            "qtiVersion": "3.0",
            "timeDependent": False,
            "adaptive": False,
            "rawXml": "<qti-assessment-item/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
        item = AssessmentItem.model_validate(data)
        assert item.identifier == "item-1"
        assert item.title == "Test Item"
        assert item.type == "choice"
        assert item.qti_version == "3.0"
        assert item.time_dependent is False
        assert item.adaptive is False

    def test_parse_with_metadata(self):
        """Should parse item with metadata."""
        data = {
            "identifier": "item-2",
            "title": "Math Question",
            "type": "text-entry",
            "qtiVersion": "3.0",
            "timeDependent": False,
            "adaptive": False,
            "rawXml": "<qti-assessment-item/>",
            "content": {},
            "metadata": {
                "subject": "Math",
                "grade": 5,
                "difficulty": "medium",
            },
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
        item = AssessmentItem.model_validate(data)
        assert item.metadata is not None
        assert item.metadata.subject == "Math"
        assert item.metadata.grade == 5
        assert item.metadata.difficulty == "medium"


class TestAssessmentTest:
    """Tests for AssessmentTest model."""

    def test_parse_minimal(self):
        """Should parse a minimal assessment test response."""
        data = {
            "identifier": "test-1",
            "title": "Unit Test",
            "qtiVersion": "3.0",
            "qti-test-part": [],
            "rawXml": "<qti-assessment-test/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
        test = AssessmentTest.model_validate(data)
        assert test.identifier == "test-1"
        assert test.title == "Unit Test"
        assert test.qti_test_part == []

    def test_parse_with_test_parts(self):
        """Should parse test with nested test parts and sections."""
        data = {
            "identifier": "test-2",
            "title": "Full Test",
            "qtiVersion": "3.0",
            "qti-test-part": [
                {
                    "identifier": "part-1",
                    "navigationMode": "linear",
                    "submissionMode": "individual",
                    "qti-assessment-section": [
                        {
                            "identifier": "section-1",
                            "title": "Section 1",
                            "visible": True,
                            "sequence": 1,
                        }
                    ],
                }
            ],
            "rawXml": "<qti-assessment-test/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
        test = AssessmentTest.model_validate(data)
        assert len(test.qti_test_part) == 1
        part = test.qti_test_part[0]
        assert part.identifier == "part-1"
        assert part.navigation_mode == "linear"
        assert len(part.qti_assessment_section) == 1


class TestStimulus:
    """Tests for Stimulus model."""

    def test_parse_minimal(self):
        """Should parse a minimal stimulus response."""
        data = {
            "identifier": "stim-1",
            "title": "Reading Passage",
            "catalogInfo": [],
            "rawXml": "<qti-assessment-stimulus/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
        stim = Stimulus.model_validate(data)
        assert stim.identifier == "stim-1"
        assert stim.title == "Reading Passage"


class TestListResponse:
    """Tests for ListResponse model."""

    def test_parse_list_response(self):
        """Should parse a paginated list response."""
        data = {
            "items": [
                {
                    "identifier": "item-1",
                    "title": "Item 1",
                    "type": "choice",
                    "qtiVersion": "3.0",
                    "timeDependent": False,
                    "adaptive": False,
                    "rawXml": "<xml/>",
                    "content": {},
                    "createdAt": "2024-01-01T00:00:00Z",
                    "updatedAt": "2024-01-01T00:00:00Z",
                }
            ],
            "total": 50,
            "page": 1,
            "pages": 5,
            "limit": 10,
            "sort": "createdAt",
            "order": "desc",
        }
        result = ListResponse[AssessmentItem].model_validate(data)
        assert result.total == 50
        assert result.page == 1
        assert result.pages == 5
        assert len(result.items) == 1
        assert result.items[0].identifier == "item-1"


class TestInputModels:
    """Tests for input validation models."""

    def test_create_item_input(self):
        """Should validate create item input."""
        data = {
            "identifier": "item-001",
            "title": "New Item",
            "type": "choice",
        }
        model = CreateAssessmentItemInput.model_validate(data)
        assert model.identifier == "item-001"
        assert model.title == "New Item"
        assert model.type == "choice"
        assert model.time_dependent is None

    def test_create_item_xml_input(self):
        """Should validate XML create input."""
        data = {
            "format": "xml",
            "xml": "<qti-assessment-item/>",
        }
        model = CreateAssessmentItemXmlInput.model_validate(data)
        assert model.format == "xml"
        assert model.xml == "<qti-assessment-item/>"

    def test_create_item_xml_rejects_empty(self):
        """Should reject empty XML."""
        with pytest.raises(ValidationError):
            CreateAssessmentItemXmlInput.model_validate({"format": "xml", "xml": ""})

    def test_create_test_input(self):
        """Should validate create test input."""
        data = {
            "identifier": "test-001",
            "title": "New Test",
            "qti-test-part": [
                {
                    "identifier": "part-1",
                    "navigationMode": "linear",
                    "submissionMode": "individual",
                    "qti-assessment-section": [],
                }
            ],
        }
        model = CreateAssessmentTestInput.model_validate(data)
        assert model.identifier == "test-001"
        assert model.title == "New Test"

    def test_create_stimulus_input(self):
        """Should validate create stimulus input."""
        data = {
            "identifier": "stim-001",
            "title": "Passage",
            "content": "<div>Reading passage</div>",
        }
        model = CreateStimulusInput.model_validate(data)
        assert model.identifier == "stim-001"
        assert model.title == "Passage"
        assert model.content == "<div>Reading passage</div>"

    def test_list_params(self):
        """Should validate list params."""
        data = {"page": 2, "limit": 25, "sort": "title", "order": "asc"}
        params = ListParams.model_validate(data)
        assert params.page == 2
        assert params.limit == 25

    def test_validate_input(self):
        """Should validate validate input."""
        data = {"schema": "item", "xml": "<xml/>"}
        model = ValidateInput.model_validate(data)
        assert model.schema_type == "item"

    def test_validate_batch_input(self):
        """Should validate batch validate input."""
        data = {
            "schema": "item",
            "xml": ["<xml1/>", "<xml2/>"],
            "entityIds": ["id-1", "id-2"],
        }
        model = ValidateBatchInput.model_validate(data)
        assert len(model.xml) == 2
        assert len(model.entity_ids) == 2

    def test_submit_lesson_feedback_input(self):
        """Should validate lesson feedback input."""
        data = {
            "userId": "user-1",
            "feedback": "Great lesson",
            "lessonId": "lesson-1",
        }
        model = SubmitLessonFeedbackInput.model_validate(data)
        assert model.user_id == "user-1"
        assert model.lesson_id == "lesson-1"

    def test_update_item_input_trims_identifier(self):
        """Should trim optional identifier when provided."""
        data = {
            "identifier": "  item-001  ",
            "title": "Updated Item",
            "type": "choice",
            "rawXml": "<qti-assessment-item/>",
            "content": {},
        }
        model = UpdateAssessmentItemInput.model_validate(data)
        assert model.identifier == "item-001"

    def test_update_item_input_rejects_whitespace_only_identifier(self):
        """Should reject whitespace-only optional identifier."""
        with pytest.raises(ValidationError):
            UpdateAssessmentItemInput.model_validate(
                {
                    "identifier": "   ",
                    "title": "Updated Item",
                    "type": "choice",
                    "rawXml": "<qti-assessment-item/>",
                    "content": {},
                }
            )

    def test_update_test_input_trims_identifier(self):
        """Should trim optional test identifier when provided."""
        data = {
            "identifier": "  test-001  ",
            "title": "Updated Test",
            "qti-test-part": [],
        }
        model = UpdateAssessmentTestInput.model_validate(data)
        assert model.identifier == "test-001"

    def test_update_stimulus_input_trims_identifier(self):
        """Should trim optional stimulus identifier when provided."""
        data = {
            "identifier": "  stim-001  ",
            "title": "Updated Stimulus",
            "content": "<div>Passage</div>",
        }
        model = UpdateStimulusInput.model_validate(data)
        assert model.identifier == "stim-001"


class TestResponseModels:
    """Tests for response models."""

    def test_delete_response(self):
        """Should parse delete response."""
        data = {"message": "Deleted successfully"}
        resp = DeleteResponse.model_validate(data)
        assert resp.message == "Deleted successfully"

    def test_delete_response_empty(self):
        """Should parse empty delete response."""
        resp = DeleteResponse.model_validate({})
        assert resp.message is None

    def test_process_response_result(self):
        """Should parse process response result."""
        data = {
            "score": 1.0,
            "feedback": {"identifier": "correct", "value": "Well done!"},
        }
        result = ProcessResponseResult.model_validate(data)
        assert result.score == 1.0
        assert result.feedback is not None
        assert result.feedback.value == "Well done!"

    def test_validation_result(self):
        """Should parse validation result."""
        data = {
            "success": True,
            "entityId": "item-1",
            "message": "Valid",
        }
        result = ValidationResult.model_validate(data)
        assert result.success is True
        assert result.entity_id == "item-1"

    def test_lesson_feedback(self):
        """Should parse lesson feedback."""
        data = {
            "userId": "user-1",
            "feedback": "Good",
            "type": "LESSON",
            "lessonId": "lesson-1",
        }
        fb = LessonFeedback.model_validate(data)
        assert fb.user_id == "user-1"
        assert fb.lesson_id == "lesson-1"
