"""Tests for the assistant's repository tools: links, FTS fallback, the new
catalogue/statistics/author/citation tools, and citation-id extraction."""

from __future__ import annotations

import uuid

import pytest

from apps.core.assistant.tools import build_tools, extract_surfaced_object_ids


def _tool(tools, name):
    return next((t for t in tools if t.name == name), None)


def _doc(collection, title, abstract="", **kw):
    from apps.repository.documents.models import Document

    pid = kw.pop("pid", f"REPO-T-{uuid.uuid4().hex[:6].upper()}")
    kw.setdefault("status", Document.Status.PUBLISHED)
    kw.setdefault("visibility", Document.Visibility.PUBLIC_OPEN)
    return Document.objects.create(
        title=title, abstract=abstract, collection=collection, public_document_id=pid, **kw
    )


@pytest.mark.django_db
def test_new_repository_tools_registered(learner_user):
    names = {t.name for t in build_tools(learner_user)}
    assert {
        "filter_repository_documents",
        "repository_statistics",
        "find_papers_by_author",
        "get_document_citation",
    }.issubset(names)


@pytest.mark.django_db
def test_search_returns_url_and_falls_back_to_fts(admin_user, public_collection):
    """With no pgvector index, search falls back to full-text search over the
    enriched tsvector — matching abstract terms (not just titles) — and every
    result carries a url."""
    from apps.repository.search.indexes import update_search_vector

    d = _doc(public_collection, "Postharvest storage trial",
             "Hermetic bags sharply reduce aflatoxin contamination in stored maize.")
    update_search_vector(d.pk)

    tool = _tool(build_tools(admin_user), "search_repository_documents")
    hits = tool.invoke({"query": "aflatoxin maize", "k": 5})  # words only in the abstract

    by_pid = {h["public_document_id"]: h for h in hits}
    assert d.public_document_id in by_pid, "FTS should match abstract terms, not only titles"
    assert "/repository/documents/" in (by_pid[d.public_document_id]["url"] or "")


@pytest.mark.django_db
def test_filter_repository_documents(admin_user, public_collection):
    from apps.repository.documents.models import Document

    thesis = _doc(public_collection, "Thesis in Uganda", "a",
                  document_type=Document.DocumentType.THESIS, country="Uganda", year=2020)
    _doc(public_collection, "Article in Kenya", "b",
         document_type=Document.DocumentType.JOURNAL_ARTICLE, country="Kenya", year=2019)

    tool = _tool(build_tools(admin_user), "filter_repository_documents")
    res = tool.invoke({"document_type": "thesis", "country": "Uganda"})
    pids = {r["public_document_id"] for r in res}
    assert thesis.public_document_id in pids
    assert len(pids) == 1
    assert all(r["url"] for r in res)


@pytest.mark.django_db
def test_repository_statistics(admin_user, public_collection):
    from apps.repository.documents.models import Document

    _doc(public_collection, "T1", "a", document_type=Document.DocumentType.THESIS, country="Uganda")
    s = _tool(build_tools(admin_user), "repository_statistics").invoke({})
    assert s["total_published"] >= 1
    for key in ("total_authors", "collections", "by_collection", "top_countries",
                "top_subjects", "most_published_authors"):
        assert key in s


@pytest.mark.django_db
def test_find_papers_by_author(admin_user, public_collection):
    from apps.repository.documents.models import Author, DocumentAuthor

    author = Author.objects.create(first_name="Anthony", last_name="Egeru")
    d = _doc(public_collection, "Rangeland dynamics in the drylands", "abc")
    DocumentAuthor.objects.create(document=d, author=author, order=0)

    tool = _tool(build_tools(admin_user), "find_papers_by_author")
    res = tool.invoke({"name": "Egeru"})
    assert res["author"] == "Anthony Egeru"
    assert res["author_url"]
    assert d.public_document_id in {p["public_document_id"] for p in res["papers"]}
    assert all(p["url"] for p in res["papers"])

    assert tool.invoke({"name": "Zzqxwv"}) == {"error": "no_author_found", "query": "Zzqxwv"}


@pytest.mark.django_db
def test_get_document_citation_formats_and_links(admin_user, public_collection):
    from apps.repository.documents.models import Author, DocumentAuthor

    author = Author.objects.create(first_name="R.", last_name="Njeru")
    d = _doc(public_collection, "Case studies on regional PhD programmes", "abc",
             year=2014, publisher="RUFORUM", pid="REPO-CITE-1")
    DocumentAuthor.objects.create(document=d, author=author, order=0)

    c = _tool(build_tools(admin_user), "get_document_citation").invoke(
        {"public_document_id": "REPO-CITE-1"})
    assert "Njeru" in c["citation"]
    assert "2014" in c["citation"]
    assert "Case studies" in c["citation"]
    assert "/repository/documents/" in (c["url"] or "")


@pytest.mark.django_db
def test_citation_respects_for_user(admin_user, learner_user, public_collection, sample_pdf):
    from apps.repository.documents.models import Document

    draft = Document.objects.create(
        title="Confidential draft", abstract="secret",
        document_type=Document.DocumentType.THESIS, status=Document.Status.DRAFT,
        collection=public_collection, submitted_by=admin_user, file=sample_pdf,
        public_document_id="REPO-CITE-DRAFT",
    )
    learner_tool = _tool(build_tools(learner_user), "get_document_citation")
    assert learner_tool.invoke({"public_document_id": draft.public_document_id}) == {
        "error": "not_found_or_not_accessible"
    }


def test_extract_surfaced_object_ids_for_new_tools():
    assert extract_surfaced_object_ids(
        "filter_repository_documents",
        [{"public_document_id": "A"}, {"public_document_id": "B"}],
    ) == ["A", "B"]
    assert extract_surfaced_object_ids(
        "find_papers_by_author",
        {"author": "x", "papers": [{"public_document_id": "P1"}, {"public_document_id": "P2"}]},
    ) == ["P1", "P2"]
    assert extract_surfaced_object_ids(
        "get_document_citation", {"public_document_id": "C"}
    ) == ["C"]
