"""Tests for Phase 11.E (realtime) and 11.G (search provider) abstractions."""

from __future__ import annotations

import dataclasses

import pytest

from simorgh.core.realtime import (
    LoggingRealtimeBackend,
    MemoryRealtimeBackend,
    publish_to_org_node,
    publish_to_user,
    publish_to_workspace,
    set_backend,
    user_room,
    workspace_room,
)
from simorgh.core.search_provider import (
    InMemorySearchProvider,
    SearchHit,
    index_document,
    remove_document,
    run_search,
    set_provider,
)

# ---------------------------------------------------------------------------
# Realtime
# ---------------------------------------------------------------------------


def test_logging_backend_is_default_and_publishes_without_error():
    set_backend(LoggingRealtimeBackend())
    publish_to_user(1, "ping", {"msg": "hi"})  # no raise
    set_backend(None)


def test_memory_backend_records_publishes():
    backend = MemoryRealtimeBackend()
    set_backend(backend)
    try:
        publish_to_user(1, "ping", {"v": 1})
        publish_to_workspace(7, "update", {"id": 99})
        publish_to_org_node(3, "alert", {"level": "high"})

        assert backend.messages[user_room(1)] == [("ping", {"v": 1})]
        assert backend.messages[workspace_room(7)] == [("update", {"id": 99})]
        assert backend.messages["org_node:3"] == [("alert", {"level": "high"})]
    finally:
        set_backend(None)


def test_memory_backend_reset():
    backend = MemoryRealtimeBackend()
    set_backend(backend)
    publish_to_user(1, "x", {})
    backend.reset()
    assert backend.messages == {}
    set_backend(None)


# ---------------------------------------------------------------------------
# Search provider
# ---------------------------------------------------------------------------


@pytest.fixture
def in_memory_provider():
    provider = InMemorySearchProvider()
    set_provider(provider)
    yield provider
    set_provider(None)


def test_index_and_search_basic(in_memory_provider):
    index_document(
        "ticket",
        "1",
        {"tenant_id": 1, "title": "Hello world", "body": "Lorem ipsum"},
    )
    hits = run_search("hello", tenant_id=1)
    assert len(hits) == 1
    assert hits[0].entity_id == "1"
    assert hits[0].entity_type == "ticket"


def test_search_filters_by_tenant(in_memory_provider):
    index_document("ticket", "1", {"tenant_id": 1, "title": "x", "body": ""})
    index_document("ticket", "2", {"tenant_id": 2, "title": "x", "body": ""})
    assert len(run_search("x", tenant_id=1)) == 1
    assert len(run_search("x", tenant_id=2)) == 1
    assert len(run_search("x")) == 2


def test_search_filters_by_entity_type(in_memory_provider):
    index_document("ticket", "1", {"tenant_id": 1, "title": "match", "body": ""})
    index_document("contact", "2", {"tenant_id": 1, "title": "match", "body": ""})
    hits = run_search("match", tenant_id=1, entity_types=("contact",))
    assert {h.entity_type for h in hits} == {"contact"}


def test_search_ranks_by_match_count(in_memory_provider):
    index_document("doc", "1", {"tenant_id": 1, "title": "rare", "body": "x x"})
    index_document(
        "doc",
        "2",
        {"tenant_id": 1, "title": "common common common", "body": "common"},
    )
    hits = run_search("common", tenant_id=1)
    assert hits[0].entity_id == "2"


def test_remove_document(in_memory_provider):
    index_document("doc", "1", {"tenant_id": 1, "title": "deleteme", "body": ""})
    assert run_search("deleteme", tenant_id=1)
    remove_document("doc", "1")
    assert not run_search("deleteme", tenant_id=1)


def test_empty_query_returns_no_hits(in_memory_provider):
    index_document("doc", "1", {"tenant_id": 1, "title": "x", "body": ""})
    assert run_search("") == []


def test_search_hit_is_immutable():
    h = SearchHit(entity_type="x", entity_id="1", title="t")
    with pytest.raises((AttributeError, TypeError, dataclasses.FrozenInstanceError)):
        h.title = "y"  # type: ignore[misc]
