"""Unit tests for oneOf model structure.

With the custom model emitter, oneOf schemas are emitted as tagged-object
models with optional typed fields — no actual_instance wrappers needed.
model_validate() works directly on server JSON.
"""

from __future__ import annotations

import json

import pytest


class TestChunkingConfiguration:
    """Test ChunkingConfiguration oneOf model."""

    def test_recursive_config(self):
        """Recursive chunking config validates and has typed fields."""
        from goodmem.models.chunking_configuration import ChunkingConfiguration

        config = ChunkingConfiguration.model_validate(
            {"recursive": {"chunkSize": 512, "chunkOverlap": 50,
                           "keepStrategy": "KEEP_END", "lengthMeasurement": "CHARACTER_COUNT"}}
        )
        assert config.recursive is not None
        assert config.recursive.chunk_size == 512
        assert config.none is None
        assert config.sentence is None

    def test_none_config(self):
        """No-chunking config validates."""
        from goodmem.models.chunking_configuration import ChunkingConfiguration

        config = ChunkingConfiguration.model_validate({"none": {}})
        assert config.none is not None
        assert config.recursive is None

    def test_roundtrip(self):
        """model_dump(by_alias=True, exclude_unset=True) produces clean JSON."""
        from goodmem.models.chunking_configuration import ChunkingConfiguration

        config = ChunkingConfiguration.model_validate(
            {"recursive": {"chunkSize": 512, "chunkOverlap": 50,
                           "keepStrategy": "KEEP_END", "lengthMeasurement": "CHARACTER_COUNT"}}
        )
        dumped = config.model_dump(by_alias=True, exclude_unset=True, mode="json")
        assert "recursive" in dumped
        assert dumped["recursive"]["chunkSize"] == 512
        # Unset fields excluded
        assert "none" not in dumped
        assert "sentence" not in dumped


class TestRetrievedItem:
    """Test RetrievedItem oneOf model (memory or chunk)."""

    def test_memory_variant(self):
        """RetrievedItem with memory key validates to typed Memory."""
        from goodmem.models.retrieved_item import RetrievedItem

        raw = {
            "memory": {
                "memoryId": "mem-1",
                "spaceId": "sp-1",
                "contentType": "text/plain",
                "processingStatus": "COMPLETED",
                "createdAt": 1704067200000,
                "updatedAt": 1704067200000,
                "createdById": "user-1",
                "updatedById": "user-1",
            }
        }
        item = RetrievedItem.model_validate(raw)
        assert item.memory is not None
        assert item.memory.memory_id == "mem-1"
        assert item.chunk is None

    def test_chunk_variant(self):
        """RetrievedItem with chunk key validates to typed ChunkReference."""
        from goodmem.models.retrieved_item import RetrievedItem

        raw = {
            "chunk": {
                "resultSetId": "rs-1",
                "chunk": {
                    "chunkId": "c-1",
                    "memoryId": "mem-1",
                    "chunkSequenceNumber": 0,
                    "chunkText": "hello world",
                    "vectorStatus": "COMPLETED",
                    "createdAt": 1704067200000,
                    "updatedAt": 1704067200000,
                    "createdById": "user-1",
                    "updatedById": "user-1",
                },
                "memoryIndex": 0,
                "relevanceScore": 0.95,
            }
        }
        item = RetrievedItem.model_validate(raw)
        assert item.chunk is not None
        assert item.chunk.chunk.chunk_id == "c-1"
        assert item.memory is None


class TestRetrieveMemoryEvent:
    """Test RetrieveMemoryEvent with nested oneOf fields."""

    def test_event_with_retrieved_item(self):
        """End-to-end: model_validate succeeds and nested fields are typed."""
        from goodmem.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {
            "retrievedItem": {
                "memory": {
                    "memoryId": "mem-1",
                    "spaceId": "sp-1",
                    "contentType": "text/plain",
                    "processingStatus": "COMPLETED",
                    "createdAt": 1704067200000,
                    "updatedAt": 1704067200000,
                    "createdById": "user-1",
                    "updatedById": "user-1",
                }
            }
        }
        event = RetrieveMemoryEvent.model_validate(raw)
        assert event.retrieved_item is not None
        assert event.retrieved_item.memory is not None
        assert event.retrieved_item.memory.memory_id == "mem-1"

    def test_null_fields_unchanged(self):
        """Null oneOf fields pass through without error."""
        from goodmem.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {"retrievedItem": None, "status": None}
        event = RetrieveMemoryEvent.model_validate(raw)
        assert event.retrieved_item is None

    def test_non_oneof_fields(self):
        """Fields that are not oneOf are properly typed."""
        from goodmem.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {
            "status": {"code": "PARTIAL_RESULTS", "message": "OK"},
        }
        event = RetrieveMemoryEvent.model_validate(raw)
        assert event.status is not None
        assert event.status.code == "PARTIAL_RESULTS"

    def test_streaming_event_parse(self, mock_client_factory):
        """Simulates parsing an NDJSON line from a retrieve stream."""
        from goodmem.models.retrieve_memory_event import RetrieveMemoryEvent

        ndjson_line = json.dumps({
            "retrievedItem": {
                "memory": {
                    "memoryId": "mem-42",
                    "spaceId": "sp-1",
                    "contentType": "text/plain",
                    "processingStatus": "COMPLETED",
                    "createdAt": 1704067200000,
                    "updatedAt": 1704067200000,
                    "createdById": "user-1",
                    "updatedById": "user-1",
                }
            }
        })

        raw = json.loads(ndjson_line)
        event = RetrieveMemoryEvent.model_validate(raw)
        assert event.retrieved_item.memory.memory_id == "mem-42"


class TestContextItem:
    """Test ContextItem oneOf model."""

    def test_text_variant(self):
        from goodmem.models.context_item import ContextItem

        item = ContextItem.model_validate({"text": "Please focus on implementation details."})
        assert item.text == "Please focus on implementation details."
        assert item.binary is None

    def test_binary_variant(self):
        from goodmem.models.context_item import ContextItem

        item = ContextItem.model_validate({
            "binary": {"contentType": "image/png", "data": "iVBORw0KGg=="}
        })
        assert item.binary is not None
        assert item.binary.content_type == "image/png"
        assert item.text is None


class TestPingEvent:
    """Test PingEvent oneOf model."""

    def test_notice_variant(self):
        from goodmem.models.ping_event import PingEvent

        event = PingEvent.model_validate({"notice": {
            "code": "SLOW_TARGET",
            "message": "Target is slow",
        }})
        assert event.notice is not None
        assert event.notice.message == "Target is slow"
        assert event.notice.code == "SLOW_TARGET"
        assert event.result is None
        assert event.summary is None
