"""Unit tests for response-side oneOf resolution.

Covers _preprocess_oneof_in_response which fixes openapi-generator's broken
oneOf deserialization (issue #17763).  Without this preprocessing,
model_validate() on responses containing oneOf wrapper fields (e.g.
RetrievedItem inside RetrieveMemoryEvent) leaves actual_instance=None.
"""

from __future__ import annotations

import json

import httpx
import pytest

from goodmem._base import _preprocess_oneof_in_response, _resolve_oneof, _is_oneof_wrapper


class TestPreprocessOneofInResponse:
    """Test response-side oneOf preprocessing on RetrieveMemoryEvent."""

    def test_retrieved_item_chunk_resolved(self):
        """RetrievedItem with 'chunk' key resolves to RetrievedItemOneOf1."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent
        from goodmem._generated.models.retrieved_item import RetrievedItem

        raw = {
            "retrievedItem": {
                "chunk": {
                    "memoryChunkId": "mc-1",
                    "content": "hello",
                }
            }
        }

        processed = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        # The retrievedItem value should now be a RetrievedItem wrapper
        ri = processed["retrievedItem"]
        assert isinstance(ri, RetrievedItem)
        assert ri.actual_instance is not None
        assert ri.actual_instance.chunk is not None

    def test_retrieved_item_memory_resolved(self):
        """RetrievedItem with 'memory' key resolves to RetrievedItemOneOf."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent
        from goodmem._generated.models.retrieved_item import RetrievedItem

        raw = {
            "retrievedItem": {
                "memory": {
                    "memoryId": "mem-1",
                    "spaceId": "sp-1",
                }
            }
        }

        processed = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        ri = processed["retrievedItem"]
        assert isinstance(ri, RetrievedItem)
        assert ri.actual_instance is not None
        assert ri.actual_instance.memory is not None

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

        raw = {"retrievedItem": None, "status": None}
        processed = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        assert processed["retrievedItem"] is None

    def test_non_oneof_fields_unchanged(self):
        """Fields that are not oneOf wrappers pass through untouched."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {
            "status": {"code": 200, "message": "OK"},
        }
        processed = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        # status is a GoodMemStatus, not a oneOf wrapper — should still be a dict
        assert isinstance(processed["status"], dict)

    def test_full_model_validate_with_chunk(self):
        """End-to-end: model_validate succeeds and .chunk is accessible."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {
            "retrievedItem": {
                "chunk": {
                    "memoryChunkId": "mc-1",
                    "content": "hello world",
                }
            }
        }

        data = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        event = RetrieveMemoryEvent.model_validate(data)
        assert event.retrieved_item is not None
        assert event.retrieved_item.actual_instance is not None
        chunk_data = event.retrieved_item.actual_instance.chunk
        assert chunk_data is not None

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

        ndjson_line = json.dumps({
            "retrievedItem": {
                "chunk": {
                    "memoryChunkId": "mc-42",
                    "content": "test content",
                }
            }
        })

        raw = json.loads(ndjson_line)
        data = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        event = RetrieveMemoryEvent.model_validate(data)
        assert event.retrieved_item.actual_instance.chunk is not None


class TestOneofGetattr:
    """Test __getattr__ delegation to actual_instance."""

    def test_chunk_accessible_directly(self):
        """retrieved_item.chunk works without going through actual_instance."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {
            "retrievedItem": {
                "chunk": {
                    "memoryChunkId": "mc-1",
                    "content": "hello",
                }
            }
        }
        data = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        event = RetrieveMemoryEvent.model_validate(data)
        # Direct access via __getattr__ delegation
        assert event.retrieved_item.chunk is not None
        assert event.retrieved_item.chunk["memoryChunkId"] == "mc-1"

    def test_memory_accessible_directly(self):
        """retrieved_item.memory works without going through actual_instance."""
        from goodmem._generated.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",
                }
            }
        }
        data = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        event = RetrieveMemoryEvent.model_validate(data)
        assert event.retrieved_item.memory is not None
        # With enough keys, _deep_resolve_any_fields resolves to a Memory model
        assert event.retrieved_item.memory.memory_id == "mem-1"

    def test_missing_attr_raises(self):
        """Accessing a nonexistent attribute still raises AttributeError."""
        from goodmem._generated.models.retrieve_memory_event import RetrieveMemoryEvent

        raw = {"retrievedItem": {"chunk": {"memoryChunkId": "mc-1"}}}
        data = _preprocess_oneof_in_response(RetrieveMemoryEvent, raw)
        event = RetrieveMemoryEvent.model_validate(data)
        with pytest.raises(AttributeError):
            _ = event.retrieved_item.nonexistent_field


class TestIsOneofWrapper:
    def test_retrieved_item_is_wrapper(self):
        from goodmem._generated.models.retrieved_item import RetrievedItem
        assert _is_oneof_wrapper(RetrievedItem)

    def test_chunking_configuration_is_wrapper(self):
        from goodmem._generated.models.chunking_configuration import ChunkingConfiguration
        assert _is_oneof_wrapper(ChunkingConfiguration)

    def test_regular_model_is_not_wrapper(self):
        from goodmem._generated.models.memory import Memory
        assert not _is_oneof_wrapper(Memory)

    def test_str_is_not_wrapper(self):
        assert not _is_oneof_wrapper(str)
