"""Tests for browse endpoint full claim field serialization.

Verifies that browse_memories() and get_timeline() return ALL SemanticClaim
fields — not just a cherry-picked subset. Issue: browse was hand-building a
14-field dict instead of serializing the full 42-field dataclass.
"""

import pytest

from cortex.storage.sqlite_adapter import SQLiteAdapter
from cortex.types.claims import SemanticClaim


@pytest.fixture
async def adapter():
    """In-memory SQLite adapter with all migrations applied."""
    a = SQLiteAdapter(db_path=":memory:")
    await a.initialize()
    yield a
    await a.close()


# ---------------------------------------------------------------------------
# Fields that were previously missing from browse
# ---------------------------------------------------------------------------

PREVIOUSLY_MISSING_FIELDS = {
    "temporal",
    "temporal_marker",
    "memory_class",
    "event_date",
    "salience",
    "cause",
    "effect",
    "state_before",
    "state_after",
    "explicitness",
    "stability",
    "entity_id",
    "entity_type",
    "mentions",
    "last_mentioned_at",
    "last_accessed",
    "superseded_by",
    "owner_id",
    "memory_type",
    "subject_entity_id",
    "object_entity_id",
    "datatype",
    "scope",
    "valid_from",
    "valid_to",
    "source_session",
    "is_deleted",
    "deleted_at",
    "flag_reason",
    "sequence_order",
}


@pytest.mark.asyncio
async def test_browse_returns_all_claim_fields(adapter: SQLiteAdapter):
    """browse_memories items should contain every SemanticClaim field."""
    await adapter.create_claim(
        subject="Andrew",
        predicate="prefers",
        object_value="dark mode",
        owner_id="test-owner",
        temporal="current",
        memory_class="semantic",
        salience="high",
        explicitness="explicit",
        stability="stable",
    )

    result = await adapter.browse_memories(owner_id="test-owner")
    assert result["total"] == 1
    item = result["items"][0]

    # Every field from SemanticClaim should be present
    dataclass_fields = set(SemanticClaim.__dataclass_fields__.keys())
    for f in dataclass_fields:
        assert f in item, f"Field '{f}' missing from browse result"


@pytest.mark.asyncio
async def test_browse_returns_previously_missing_fields(adapter: SQLiteAdapter):
    """Specifically check the fields that were missing before the fix."""
    await adapter.create_claim(
        subject="Eva",
        predicate="location",
        object_value="Bangkok",
        owner_id="test-owner",
        temporal="current",
        temporal_marker="now",
        memory_class="episodic",
        event_date="2026-03-20",
        salience="high",
        cause="relocation",
        effect="new timezone",
        state_before="Tokyo",
        state_after="Bangkok",
        explicitness="explicit",
        stability="stable",
        entity_id="eva",
    )

    result = await adapter.browse_memories(owner_id="test-owner")
    item = result["items"][0]

    for f in PREVIOUSLY_MISSING_FIELDS:
        assert f in item, f"Previously missing field '{f}' still absent from browse"


@pytest.mark.asyncio
async def test_browse_populated_field_values(adapter: SQLiteAdapter):
    """Verify that populated fields carry their actual values, not None."""
    await adapter.create_claim(
        subject="Andrew",
        predicate="likes",
        object_value="coffee",
        owner_id="test-owner",
        temporal="current",
        memory_class="semantic",
        salience="high",
        explicitness="explicit",
        stability="stable",
        cause="caffeine dependency",
        effect="alertness",
        event_date="2026-03-20",
        entity_id="andrew",
    )

    result = await adapter.browse_memories(owner_id="test-owner")
    item = result["items"][0]

    assert item["temporal"] == "current"
    assert item["memory_class"] == "semantic"
    assert item["salience"] == "high"
    assert item["explicitness"] == "explicit"
    assert item["stability"] == "stable"
    assert item["cause"] == "caffeine dependency"
    assert item["effect"] == "alertness"
    assert item["event_date"] == "2026-03-20"
    assert item["entity_id"] == "andrew"


@pytest.mark.asyncio
async def test_browse_preserves_computed_fields(adapter: SQLiteAdapter):
    """content and object_value should still be computed/truncated."""
    long_obj = "x" * 500
    await adapter.create_claim(
        subject="Eva",
        predicate="says",
        object_value=long_obj,
        owner_id="test-owner",
    )

    result = await adapter.browse_memories(owner_id="test-owner")
    item = result["items"][0]

    # object_value should be truncated to 200 chars for browse
    assert len(item["object_value"]) == 200
    # content is the computed summary: "subject predicate object_preview"
    assert item["content"].startswith("Eva says")


@pytest.mark.asyncio
async def test_browse_none_fields_serialized(adapter: SQLiteAdapter):
    """Optional fields with None values should still appear as None, not be omitted."""
    await adapter.create_claim(
        subject="test",
        predicate="test",
        object_value="test",
        owner_id="test-owner",
    )

    result = await adapter.browse_memories(owner_id="test-owner")
    item = result["items"][0]

    # These optional fields should be None but present
    assert "cause" in item
    assert item["cause"] is None
    assert "effect" in item
    assert item["effect"] is None
    assert "event_date" in item
    assert item["event_date"] is None
    assert "superseded_by" in item
    assert item["superseded_by"] is None
    assert "entity_id" in item


# ---------------------------------------------------------------------------
# Timeline tests
# ---------------------------------------------------------------------------


@pytest.mark.asyncio
async def test_timeline_returns_all_claim_fields(adapter: SQLiteAdapter):
    """get_timeline items should contain every SemanticClaim field."""
    await adapter.create_claim(
        subject="Andrew",
        predicate="location",
        object_value="Bangkok",
        owner_id="test-owner",
        temporal="current",
        memory_class="episodic",
        salience="high",
        cause="moved",
        effect="new life",
        state_before="Tokyo",
        state_after="Bangkok",
        explicitness="explicit",
        stability="mutable",
    )

    result = await adapter.get_timeline(subject="Andrew", owner_id="test-owner")
    assert len(result) == 1
    item = result[0]

    # Every field from SemanticClaim should be present
    dataclass_fields = set(SemanticClaim.__dataclass_fields__.keys())
    for f in dataclass_fields:
        assert f in item, f"Field '{f}' missing from timeline result"


@pytest.mark.asyncio
async def test_timeline_populated_values(adapter: SQLiteAdapter):
    """Timeline should carry actual field values, not just keys."""
    await adapter.create_claim(
        subject="Eva",
        predicate="status",
        object_value="online",
        owner_id="test-owner",
        temporal="current",
        temporal_marker="right now",
        memory_class="episodic",
        salience="high",
        cause="boot",
        effect="operational",
        state_before="offline",
        state_after="online",
    )

    result = await adapter.get_timeline(subject="Eva", owner_id="test-owner")
    item = result[0]

    assert item["temporal"] == "current"
    assert item["temporal_marker"] == "right now"
    assert item["memory_class"] == "episodic"
    assert item["salience"] == "high"
    assert item["cause"] == "boot"
    assert item["effect"] == "operational"
    assert item["state_before"] == "offline"
    assert item["state_after"] == "online"


@pytest.mark.asyncio
async def test_timeline_mentions_and_entity_fields(adapter: SQLiteAdapter):
    """Timeline should include mention tracking and entity scoping fields."""
    await adapter.create_claim(
        subject="Andrew",
        predicate="uses",
        object_value="VSCode",
        owner_id="test-owner",
        entity_id="andrew",
        entity_type="human",
    )

    result = await adapter.get_timeline(subject="Andrew", owner_id="test-owner")
    item = result[0]

    assert "mentions" in item
    assert "last_mentioned_at" in item
    assert "entity_id" in item
    assert item["entity_id"] == "andrew"
    assert "entity_type" in item
    assert item["entity_type"] == "human"
    assert "last_accessed" in item
    assert "superseded_by" in item
