"""Recording transport for fixture-driven QTI tests."""

from __future__ import annotations

from typing import Any

from test_support.transport_base import BaseRecordingTransport
from test_support.transport_helpers import dump_model
from timeback_common import QtiPaths

from ..types.responses import (
    AssessmentItem,
    AssessmentItemRef,
    AssessmentSection,
    AssessmentTest,
    BatchValidationResult,
    LessonFeedback,
    QuestionsResponse,
    Stimulus,
    TestPart,
    ValidationResult,
)


def create_recording_transport(
    fail_request: dict[str, Any] | None = None,
    transport_config: dict[str, Any] | None = None,
) -> QtiRecordingTransport:
    """Create a transport-like object that records calls and returns stub responses."""
    return QtiRecordingTransport(
        fail_request=fail_request,
        transport_config=transport_config,
    )


class QtiRecordingTransport(BaseRecordingTransport):
    """QTI-specific recording transport with resource-scoped stubs."""

    def __init__(self, **kwargs: Any) -> None:
        super().__init__(**kwargs)
        self.paths = QtiPaths()
        self._apply_exclude_paths()

    def _default_get_stub(self, path: str) -> dict[str, Any]:
        for resolver in (
            _assessment_items_get_stub,
            _assessment_tests_get_stub,
            _stimuli_get_stub,
            _lesson_get_stub,
        ):
            response = resolver(path)
            if response is not None:
                return response
        return {}

    def _default_post_stub(self, path: str) -> dict[str, Any]:
        for resolver in (
            _validate_post_stub,
            _lesson_post_stub,
            _assessment_items_post_stub,
            _assessment_tests_post_stub,
            _stimuli_post_stub,
        ):
            response = resolver(path)
            if response is not None:
                return response
        return {}

    def _default_put_stub(self, path: str) -> dict[str, Any]:
        for resolver in (
            _assessment_items_put_stub,
            _assessment_tests_put_stub,
            _stimuli_put_stub,
        ):
            response = resolver(path)
            if response is not None:
                return response
        return {}

    def _default_delete_stub(self, path: str) -> dict[str, Any]:
        del path
        return {"message": "Deleted successfully"}


def _qti_list_response(items: list[dict[str, Any]]) -> dict[str, Any]:
    return {
        "items": items,
        "total": len(items),
        "page": 1,
        "pages": 1,
        "limit": 100,
        "sort": "createdAt",
        "order": "desc",
    }


def _validation_result_stub() -> ValidationResult:
    return ValidationResult.model_validate(
        {"success": True, "entityId": "item-001", "message": "Valid"}
    )


def _batch_validation_result_stub() -> BatchValidationResult:
    return BatchValidationResult.model_validate(
        {"results": [dump_model(_validation_result_stub(), exclude_none=True)]}
    )


def _validate_post_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/validate"):
        return dump_model(_validation_result_stub(), exclude_none=True)
    if path.endswith("/validate/batch"):
        return dump_model(_batch_validation_result_stub(), exclude_none=True)
    if path.endswith("/process-response"):
        return {"score": 1.0, "feedback": {"identifier": "correct", "value": "Well done"}}
    return None


def _stimulus_stub() -> Stimulus:
    return Stimulus.model_validate(
        {
            "identifier": "stimulus-001",
            "title": "Stub Stimulus",
            "catalogInfo": [],
            "rawXml": "<qti-assessment-stimulus/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
    )


def _stimuli_get_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/stimuli"):
        return _qti_list_response([dump_model(_stimulus_stub(), exclude_none=True)])
    if "/stimuli/" in path:
        return dump_model(_stimulus_stub(), exclude_none=True)
    return None


def _stimuli_post_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/stimuli"):
        return dump_model(_stimulus_stub(), exclude_none=True)
    return None


def _stimuli_put_stub(path: str) -> dict[str, Any] | None:
    if "/stimuli/" in path:
        return dump_model(_stimulus_stub(), exclude_none=True)
    return None


def _lesson_feedback_stub() -> LessonFeedback:
    return LessonFeedback.model_validate(
        {
            "userId": "user-001",
            "feedback": "Looks good",
            "type": "LESSON",
            "lessonId": "lesson-001",
        }
    )


def _lesson_get_stub(path: str) -> dict[str, Any] | None:
    if "/lesson/" in path:
        return dump_model(_lesson_feedback_stub(), exclude_none=True)
    return None


def _lesson_post_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/lesson") or path.endswith("/question"):
        return dump_model(_lesson_feedback_stub(), exclude_none=True)
    return None


def _assessment_item_stub() -> AssessmentItem:
    return AssessmentItem.model_validate(
        {
            "identifier": "item-001",
            "title": "Stub 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",
        }
    )


def _assessment_items_get_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/assessment-items"):
        return _qti_list_response([dump_model(_assessment_item_stub(), exclude_none=True)])
    if "/assessment-items/" in path:
        return dump_model(_assessment_item_stub(), exclude_none=True)
    return None


def _assessment_items_post_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/assessment-items") or path.endswith("/assessment-items/metadata"):
        return dump_model(_assessment_item_stub(), exclude_none=True)
    return None


def _assessment_items_put_stub(path: str) -> dict[str, Any] | None:
    if "/assessment-items/" in path:
        return dump_model(_assessment_item_stub(), exclude_none=True)
    return None


def _assessment_item_ref_stub() -> AssessmentItemRef:
    return AssessmentItemRef.model_validate(
        {
            "identifier": "item-001",
            "href": "/assessment-items/item-001",
            "sequence": 1,
        }
    )


def _assessment_section_stub() -> AssessmentSection:
    return AssessmentSection.model_validate(
        {
            "identifier": "section-001",
            "title": "Section 1",
            "visible": True,
            "required": False,
            "fixed": False,
            "sequence": 1,
            "qti-assessment-item-ref": [dump_model(_assessment_item_ref_stub(), exclude_none=True)],
        }
    )


def _test_part_stub() -> TestPart:
    return TestPart.model_validate(
        {
            "identifier": "part-001",
            "navigationMode": "linear",
            "submissionMode": "individual",
            "qti-assessment-section": [dump_model(_assessment_section_stub(), exclude_none=True)],
        }
    )


def _assessment_test_stub() -> AssessmentTest:
    return AssessmentTest.model_validate(
        {
            "identifier": "test-001",
            "title": "Stub Test",
            "qtiVersion": "3.0",
            "qti-test-part": [dump_model(_test_part_stub(), exclude_none=True)],
            "rawXml": "<qti-assessment-test/>",
            "content": {},
            "createdAt": "2024-01-01T00:00:00Z",
            "updatedAt": "2024-01-01T00:00:00Z",
        }
    )


def _questions_response_stub() -> QuestionsResponse:
    return QuestionsResponse.model_validate(
        {
            "assessmentTest": "test-001",
            "title": "Stub Test",
            "totalQuestions": 1,
            "questions": [
                {
                    "reference": {
                        "identifier": "item-001",
                        "href": "/assessment-items/item-001",
                        "testPart": "part-001",
                        "section": "section-001",
                    },
                    "question": dump_model(_assessment_item_stub(), exclude_none=True),
                }
            ],
        }
    )


def _assessment_tests_get_stub(path: str) -> dict[str, Any] | None:
    if "/test-parts/" in path and path.endswith("/sections"):
        return _qti_list_response([dump_model(_assessment_section_stub(), exclude_none=True)])
    if "/sections/" in path and path.endswith("/items"):
        return _qti_list_response([dump_model(_assessment_item_ref_stub(), exclude_none=True)])
    if "/sections/" in path:
        return dump_model(_assessment_section_stub(), exclude_none=True)
    if path.endswith("/test-parts"):
        return _qti_list_response([dump_model(_test_part_stub(), exclude_none=True)])
    if "/test-parts/" in path:
        return dump_model(_test_part_stub(), exclude_none=True)
    if path.endswith("/assessment-tests"):
        return _qti_list_response([dump_model(_assessment_test_stub(), exclude_none=True)])
    if path.endswith("/questions"):
        return dump_model(_questions_response_stub(), exclude_none=True)
    if "/assessment-tests/" in path:
        return dump_model(_assessment_test_stub(), exclude_none=True)
    return None


def _assessment_tests_post_stub(path: str) -> dict[str, Any] | None:
    if path.endswith("/assessment-tests"):
        return dump_model(_assessment_test_stub(), exclude_none=True)
    if path.endswith("/test-parts"):
        return dump_model(_test_part_stub(), exclude_none=True)
    if path.endswith("/sections") or path.endswith("/items"):
        return dump_model(_assessment_section_stub(), exclude_none=True)
    return None


def _assessment_tests_put_stub(path: str) -> dict[str, Any] | None:
    if "/test-parts/" in path and path.endswith("/order"):
        return dump_model(_assessment_section_stub(), exclude_none=True)
    if "/test-parts/" in path and "/sections/" in path:
        return dump_model(_assessment_section_stub(), exclude_none=True)
    if "/test-parts/" in path:
        return dump_model(_test_part_stub(), exclude_none=True)
    if path.endswith("/assessment-tests") or "/assessment-tests/" in path:
        return dump_model(_assessment_test_stub(), exclude_none=True)
    return None


__all__ = ["QtiRecordingTransport", "create_recording_transport"]
