"""Tests for CASE type validation."""

import pytest
from pydantic import ValidationError

from timeback_case.types.inputs import (
    CFPackageInput,
)
from timeback_case.types.responses import (
    CFAssociation,
    CFDocument,
    CFItem,
    CFPackage,
    CFPackageUploadResult,
)


def _minimal_package_dict() -> dict:
    """Build a minimal valid CASE package input dict."""
    return {
        "CFDocument": {
            "identifier": "doc-001",
            "uri": "urn:doc-001",
            "lastChangeDateTime": "2024-01-01T00:00:00Z",
            "title": "Test Framework",
            "creator": "Test Org",
        },
        "CFItems": [
            {
                "identifier": "item-001",
                "uri": "urn:item-001",
                "lastChangeDateTime": "2024-01-01T00:00:00Z",
                "fullStatement": "Student can demonstrate test concepts",
            },
        ],
        "CFAssociations": [
            {
                "identifier": "assoc-001",
                "uri": "urn:assoc-001",
                "lastChangeDateTime": "2024-01-01T00:00:00Z",
                "associationType": "isChildOf",
                "originNodeURI": {
                    "title": "Test Item",
                    "identifier": "item-001",
                    "uri": "urn:item-001",
                },
                "destinationNodeURI": {
                    "title": "Test Doc",
                    "identifier": "doc-001",
                    "uri": "urn:doc-001",
                },
            },
        ],
    }


class TestCFPackageInput:
    """Tests for CFPackageInput validation."""

    def test_valid_minimal_package(self):
        """Should accept a valid minimal package."""
        data = _minimal_package_dict()
        model = CFPackageInput.model_validate(data)
        assert model.cf_document.identifier == "doc-001"
        assert len(model.cf_items) == 1
        assert len(model.cf_associations) == 1

    def test_reject_empty_document_identifier(self):
        """Should reject package with empty document identifier."""
        data = _minimal_package_dict()
        data["CFDocument"]["identifier"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_document_title(self):
        """Should reject package with empty document title."""
        data = _minimal_package_dict()
        data["CFDocument"]["title"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_document_uri(self):
        """Should reject package with empty document uri."""
        data = _minimal_package_dict()
        data["CFDocument"]["uri"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_document_creator(self):
        """Should reject package with empty document creator."""
        data = _minimal_package_dict()
        data["CFDocument"]["creator"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_item_identifier(self):
        """Should reject package with empty item identifier."""
        data = _minimal_package_dict()
        data["CFItems"][0]["identifier"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_item_full_statement(self):
        """Should reject package with empty item fullStatement."""
        data = _minimal_package_dict()
        data["CFItems"][0]["fullStatement"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_association_identifier(self):
        """Should reject package with empty association identifier."""
        data = _minimal_package_dict()
        data["CFAssociations"][0]["identifier"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_association_type(self):
        """Should reject package with empty association type."""
        data = _minimal_package_dict()
        data["CFAssociations"][0]["associationType"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_node_uri_title(self):
        """Should reject package with empty node URI title."""
        data = _minimal_package_dict()
        data["CFAssociations"][0]["originNodeURI"]["title"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_reject_empty_node_uri_identifier(self):
        """Should reject package with empty originNodeURI identifier."""
        data = _minimal_package_dict()
        data["CFAssociations"][0]["originNodeURI"]["identifier"] = ""
        with pytest.raises(ValidationError):
            CFPackageInput.model_validate(data)

    def test_serialization_round_trip(self):
        """Should serialize and deserialize correctly."""
        data = _minimal_package_dict()
        model = CFPackageInput.model_validate(data)
        dumped = model.model_dump(mode="json", by_alias=True, exclude_none=True)
        assert dumped["CFDocument"]["identifier"] == "doc-001"
        assert dumped["CFDocument"]["title"] == "Test Framework"
        assert dumped["CFItems"][0]["fullStatement"] == "Student can demonstrate test concepts"
        assert dumped["CFAssociations"][0]["associationType"] == "isChildOf"

    def test_optional_fields(self):
        """Should accept package with optional fields."""
        data = _minimal_package_dict()
        data["CFDocument"]["description"] = "A test framework"
        data["CFDocument"]["publisher"] = "Test Publisher"
        data["CFItems"][0]["humanCodingScheme"] = "1.1"
        data["CFAssociations"][0]["sequenceNumber"] = 1
        model = CFPackageInput.model_validate(data)
        assert model.cf_document.description == "A test framework"
        assert model.cf_items[0].human_coding_scheme == "1.1"


class TestCFDocumentResponse:
    """Tests for CFDocument response model."""

    def test_parse_document(self):
        """Should parse a document response."""
        data = {
            "sourcedId": "doc-sourced-001",
            "title": "Test Framework",
            "uri": "urn:doc-001",
            "creator": "Test Org",
            "lastChangeDateTime": "2024-01-01T00:00:00Z",
            "CFPackageURI": {
                "sourcedId": "doc-sourced-001",
                "title": "Test Framework",
                "uri": "urn:doc-001",
            },
        }
        model = CFDocument.model_validate(data)
        assert model.sourced_id == "doc-sourced-001"
        assert model.title == "Test Framework"
        assert model.cf_package_uri.sourced_id == "doc-sourced-001"

    def test_parse_document_with_optional(self):
        """Should parse a document with optional fields."""
        data = {
            "sourcedId": "doc-sourced-001",
            "title": "Test Framework",
            "uri": "urn:doc-001",
            "creator": "Test Org",
            "lastChangeDateTime": "2024-01-01T00:00:00Z",
            "CFPackageURI": {
                "sourcedId": "doc-sourced-001",
                "title": "Test Framework",
                "uri": "urn:doc-001",
            },
            "publisher": "Test Publisher",
            "description": "A test framework",
            "language": "en",
        }
        model = CFDocument.model_validate(data)
        assert model.publisher == "Test Publisher"
        assert model.language == "en"


class TestCFItemResponse:
    """Tests for CFItem response model."""

    def test_parse_item(self):
        """Should parse an item response."""
        data = {
            "sourcedId": "item-sourced-001",
            "fullStatement": "Test competency",
            "CFItemType": "Standard",
            "uri": "urn:item-001",
            "lastChangeDateTime": "2024-01-01T00:00:00Z",
        }
        model = CFItem.model_validate(data)
        assert model.sourced_id == "item-sourced-001"
        assert model.full_statement == "Test competency"
        assert model.cf_item_type == "Standard"


class TestCFAssociationResponse:
    """Tests for CFAssociation response model."""

    def test_parse_association(self):
        """Should parse an association response."""
        data = {
            "sourcedId": "assoc-sourced-001",
            "associationType": "isChildOf",
            "uri": "urn:assoc-001",
            "originNodeURI": {
                "sourcedId": "item-001",
                "title": "Item",
                "uri": "urn:item-001",
            },
            "destinationNodeURI": {
                "sourcedId": "doc-001",
                "title": "Doc",
                "uri": "urn:doc-001",
            },
            "lastChangeDateTime": "2024-01-01T00:00:00Z",
        }
        model = CFAssociation.model_validate(data)
        assert model.sourced_id == "assoc-sourced-001"
        assert model.association_type == "isChildOf"
        assert model.origin_node_uri.sourced_id == "item-001"
        assert model.destination_node_uri.sourced_id == "doc-001"


class TestCFPackageResponse:
    """Tests for CFPackage response model."""

    def test_parse_package(self):
        """Should parse a package response."""
        data = {
            "sourcedId": "pkg-001",
            "CFDocument": {
                "sourcedId": "doc-001",
                "title": "Test Framework",
                "uri": "urn:doc-001",
                "creator": "Test Org",
                "lastChangeDateTime": "2024-01-01T00:00:00Z",
                "CFPackageURI": {
                    "sourcedId": "doc-001",
                    "title": "Test Framework",
                    "uri": "urn:doc-001",
                },
            },
            "CFItems": [
                {
                    "sourcedId": "item-001",
                    "fullStatement": "Test",
                    "CFItemType": "Standard",
                    "uri": "urn:item-001",
                    "lastChangeDateTime": "2024-01-01T00:00:00Z",
                }
            ],
        }
        model = CFPackage.model_validate(data)
        assert model.sourced_id == "pkg-001"
        assert model.cf_document.title == "Test Framework"
        assert len(model.cf_items) == 1


class TestCFPackageUploadResult:
    """Tests for CFPackageUploadResult response model."""

    def test_parse_upload_result(self):
        """Should parse an upload result response."""
        data = {
            "success": True,
            "message": "Package uploaded",
            "result": {
                "documentId": "doc-001",
                "stats": {
                    "documents": 1,
                    "items": 5,
                    "associations": 4,
                },
                "success": True,
            },
        }
        model = CFPackageUploadResult.model_validate(data)
        assert model.success is True
        assert model.result.document_id == "doc-001"
        assert model.result.stats.documents == 1
        assert model.result.stats.items == 5
        assert model.result.stats.associations == 4
