from wagtail.search.backends import get_search_backend

from tests.conftest import EventPageFactory, GenericPageFactory


SEARCH_QUERY = """
query Search($query: String) {
  results: search(query: $query) {
    __typename
    ... on PageInterface {
      title
    }
  }
}
"""


def _index(page):
    # Wagtail's post_save signal enqueues indexing via django-tasks, which isn't
    # drained synchronously in tests. Call the backend directly so the page is
    # findable through the live search code path.
    get_search_backend().add(page)


def _titles_for(body, typename):
    return [r["title"] for r in body["data"]["results"] if r["__typename"] == typename]


def test_search_returns_live_generic_page(home_page, graphql_post):
    page = GenericPageFactory(
        parent=home_page,
        title="PublishedGenericSearchToken",
        slug="published-generic-search",
    )
    _index(page)

    response, body = graphql_post(SEARCH_QUERY, {"query": "PublishedGenericSearchToken"})

    assert response.status_code == 200
    assert "errors" not in body, body
    assert "PublishedGenericSearchToken" in _titles_for(body, "GenericPage")


def test_search_excludes_draft_generic_page(home_page, graphql_post):
    page = GenericPageFactory(
        parent=home_page,
        title="DraftGenericSearchToken",
        slug="draft-generic-search",
        live=False,
    )
    _index(page)

    response, body = graphql_post(SEARCH_QUERY, {"query": "DraftGenericSearchToken"})

    assert response.status_code == 200
    assert "errors" not in body, body
    assert "DraftGenericSearchToken" not in _titles_for(body, "GenericPage")


def test_search_returns_live_event_page(home_page, event_index, graphql_post):
    page = EventPageFactory(
        parent=event_index,
        title="PublishedEventSearchToken",
        slug="published-event-search",
    )
    _index(page)

    response, body = graphql_post(SEARCH_QUERY, {"query": "PublishedEventSearchToken"})

    assert response.status_code == 200
    assert "errors" not in body, body
    assert "PublishedEventSearchToken" in _titles_for(body, "EventPage")


def test_search_excludes_draft_event_page(home_page, event_index, graphql_post):
    page = EventPageFactory(
        parent=event_index,
        title="DraftEventSearchToken",
        slug="draft-event-search",
        live=False,
    )
    _index(page)

    response, body = graphql_post(SEARCH_QUERY, {"query": "DraftEventSearchToken"})

    assert response.status_code == 200
    assert "errors" not in body, body
    assert "DraftEventSearchToken" not in _titles_for(body, "EventPage")


def test_search_results_not_grouped_by_type(home_page, event_index, graphql_post):
    # Two pages of different types matching the query equally, plus a third
    # page of one of those types that should rank highest. Under the
    # per-model-iteration resolver, all Generic results come before all Event
    # results (or vice versa) — type-grouped — so the highest-relevance Event
    # ends up after a less-relevant Generic. Cross-type relevance ordering
    # should put the strongest match first regardless of type.
    weak_generic = GenericPageFactory(
        parent=home_page,
        title="Klatremus klatremus klatremus",
        slug="weak-generic",
    )
    weak_event = EventPageFactory(
        parent=event_index,
        title="Klatremus klatremus klatremus",
        slug="weak-event",
    )
    strong_event = EventPageFactory(
        parent=event_index,
        title="Klatremus klatremus klatremus klatremus klatremus klatremus",
        slug="strong-event",
    )
    _index(weak_generic)
    _index(weak_event)
    _index(strong_event)

    response, body = graphql_post(SEARCH_QUERY, {"query": "klatremus"})

    assert response.status_code == 200
    assert "errors" not in body, body
    order = [
        (r["__typename"], r["title"])
        for r in body["data"]["results"]
        if r["__typename"] in ("GenericPage", "EventPage")
    ]
    assert len(order) == 3, order
    # Per-type grouping would put all results of one type consecutively
    # before the other type. Cross-type relevance ordering should interleave.
    types_seen = [t for t, _ in order]
    assert types_seen != ["GenericPage", "EventPage", "EventPage"], order
    assert types_seen != ["EventPage", "EventPage", "GenericPage"], order
