"""Unit tests for common/core_search.py — the CORE typed-source foundation.

Pure unit: normalization of a CORE v3 `output` object, DOI/arxiv extraction, the
core.ac.uk URL recogniser, the rate-limit pacing, and the fail-closed key gate are
exercised WITHOUT any network. conftest.py puts common/ + the infra root on sys.path
and stubs boto3, so `import core_search` is import-light.
"""
from __future__ import annotations

import time

import core_search as cs


# ── DOI extraction ─────────────────────────────────────────────────────────────

def test_extract_doi_bare():
    assert cs.extract_doi("10.1145/3292500.3330701") == "10.1145/3292500.3330701"


def test_extract_doi_from_url():
    assert cs.extract_doi("https://doi.org/10.1038/s41586-021-03819-2") == \
        "10.1038/s41586-021-03819-2"


def test_extract_doi_none():
    assert cs.extract_doi("no doi here") is None
    assert cs.extract_doi(None) is None


# ── CORE work normalization ─────────────────────────────────────────────────────

def _sample_output():
    return {
        "id": 12345678,
        "title": "Attention   Is\nAll You Need",
        "authors": [{"name": "Ashish Vaswani"}, {"name": "Noam Shazeer"}],
        "abstract": "The dominant   sequence transduction models …",
        "yearPublished": "2017",
        "publishedDate": "2017-06-12T00:00:00",
        "doi": "10.5555/3295222.3295349",
        "arxivId": "1706.03762",
        "downloadUrl": "https://core.ac.uk/download/12345678.pdf",
        "publisher": "Curran Associates",
        "language": {"code": "en", "name": "English"},
        "documentType": "research",
        "oai": "oai:arXiv.org:1706.03762",
        "dataProviders": [{"id": 1, "name": "arXiv"}, {"id": 2, "name": "MIT DSpace"}],
        "links": [{"type": "download", "url": "https://core.ac.uk/download/12345678.pdf"}],
    }


def test_from_api_core_fields():
    w = cs.CoreWork.from_api(_sample_output())
    assert w.core_id == "12345678"
    assert w.title == "Attention Is All You Need"          # whitespace collapsed
    assert [a.name for a in w.authors] == ["Ashish Vaswani", "Noam Shazeer"]
    assert w.year == 2017
    assert w.doi == "10.5555/3295222.3295349"
    assert w.arxiv_id == "1706.03762"                       # normalized via arxiv.py
    assert w.full_text_url == "https://core.ac.uk/download/12345678.pdf"
    assert w.language == "en"
    assert w.publisher == "Curran Associates"
    assert w.data_providers == ["arXiv", "MIT DSpace"]
    assert w.provider == "core"
    assert w.core_url == "https://core.ac.uk/works/12345678"


def test_from_api_fulltext_url_falls_back_to_source():
    obj = _sample_output()
    del obj["downloadUrl"]
    obj["sourceFulltextUrls"] = ["https://repo.example/paper.pdf"]
    w = cs.CoreWork.from_api(obj)
    assert w.full_text_url == "https://repo.example/paper.pdf"


def test_from_api_publisher_from_journals():
    obj = _sample_output()
    del obj["publisher"]
    obj["journals"] = [{"title": "NeurIPS Proceedings"}]
    w = cs.CoreWork.from_api(obj)
    assert w.publisher == "NeurIPS Proceedings"


def test_to_metadata_shape():
    w = cs.CoreWork.from_api(_sample_output())
    meta = w.to_metadata()
    assert meta["source_type"] == "paper"
    assert meta["provider"] == "core"
    assert meta["title"] == "Attention Is All You Need"
    assert meta["core"]["core_id"] == "12345678"


# ── core.ac.uk recogniser ───────────────────────────────────────────────────────

def test_recognize_core_works_url():
    assert cs.recognize_core_id("https://core.ac.uk/works/98765") == "98765"


def test_recognize_core_download_url():
    assert cs.recognize_core_id("https://core.ac.uk/download/42.pdf") == "42"


def test_recognize_core_prefix():
    assert cs.recognize_core_id("see core:55512 for the record") == "55512"


def test_recognize_core_bare_int_not_matched():
    # a bare integer is too ambiguous to route — only explicit shapes match
    assert cs.recognize_core_id("the number 12345") is None


# ── best_body foundation ────────────────────────────────────────────────────────

def test_best_body_includes_title_abstract_provenance():
    w = cs.CoreWork.from_api(_sample_output())
    body = cs.best_body(w)
    assert "Attention Is All You Need" in body
    assert "DOI: 10.5555/3295222.3295349" in body
    assert "Source repositories: arXiv, MIT DSpace" in body


# ── fail-closed key gate ────────────────────────────────────────────────────────

def test_client_fail_closed_without_key(monkeypatch):
    monkeypatch.delenv("CORE_API_KEY", raising=False)
    c = cs.CoreClient(api_key="")
    assert c.has_key is False
    assert c.key_prefix() == "<unset>"
    import pytest
    with pytest.raises(cs.CoreAuthError):
        c.search_works("anything")


def test_key_prefix_never_full_secret():
    c = cs.CoreClient(api_key="supersecret-key-value")
    p = c.key_prefix()
    assert "supersecret" not in p
    assert p == "supe…"


# ── rate-limit pacing ───────────────────────────────────────────────────────────

def test_throttle_paces_calls():
    c = cs.CoreClient(api_key="k", min_interval=0.2)
    t0 = time.monotonic()
    c._throttle()          # first is immediate
    c._throttle()          # second waits ~min_interval
    elapsed = time.monotonic() - t0
    assert elapsed >= 0.18
