"""Tests for huntpdf.resolvers.doi."""

from pathlib import Path
from unittest.mock import MagicMock

import pytest
import respx
from httpx import Response

from huntpdf.errors import DownloadFailed, PDFNotFound
from huntpdf.resolvers.doi import resolve_doi
from huntpdf.scholar import ScholarResult


_SAMPLE_DOI = "10.1038/s41586-020-2649-2"
_UNPAYWALL_URL = f"https://api.unpaywall.org/v2/{_SAMPLE_DOI}"
_S2_URL = f"https://api.semanticscholar.org/graph/v1/paper/DOI:{_SAMPLE_DOI}"


@pytest.fixture(autouse=True)
def _set_email(monkeypatch):
    monkeypatch.setenv("UNPAYWALL_EMAIL", "test@example.com")


class TestResolveDoi:
    @respx.mock
    def test_oa_pdf_found(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(
                200,
                json={
                    "best_oa_location": {
                        "url_for_pdf": "https://example.com/paper.pdf"
                    }
                },
            )
        )
        mock_download = MagicMock(return_value=tmp_path / "paper.pdf")
        monkeypatch.setattr("huntpdf.resolvers.doi.download_pdf", mock_download)

        result = resolve_doi(_SAMPLE_DOI, tmp_path / "paper.pdf")
        assert result == tmp_path / "paper.pdf"
        mock_download.assert_called_once_with(
            "https://example.com/paper.pdf", tmp_path / "paper.pdf"
        )

    @respx.mock
    def test_no_oa_location_falls_back_to_s2(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(200, json={"best_oa_location": None})
        )
        expected = tmp_path / "paper.pdf"
        mock_arxiv = MagicMock(return_value=expected)
        monkeypatch.setattr("huntpdf.resolvers.arxiv.resolve_arxiv", mock_arxiv)
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi",
            MagicMock(return_value=ScholarResult(
                open_access_url=None, arxiv_id="2301.07041", pmcid=None,
            )),
        )

        result = resolve_doi(_SAMPLE_DOI, expected)
        assert result == expected
        mock_arxiv.assert_called_once_with("2301.07041", expected)

    @respx.mock
    def test_download_fails_falls_back_to_s2_arxiv(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(
                200,
                json={"best_oa_location": {"url_for_pdf": "https://paywall.com/paper.pdf"}},
            )
        )
        mock_download = MagicMock(side_effect=DownloadFailed("paywall"))
        monkeypatch.setattr("huntpdf.resolvers.doi.download_pdf", mock_download)

        expected = tmp_path / "paper.pdf"
        mock_arxiv = MagicMock(return_value=expected)
        monkeypatch.setattr("huntpdf.resolvers.arxiv.resolve_arxiv", mock_arxiv)
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi",
            MagicMock(return_value=ScholarResult(
                open_access_url=None, arxiv_id="1304.1068", pmcid=None,
            )),
        )

        result = resolve_doi(_SAMPLE_DOI, expected)
        assert result == expected
        mock_arxiv.assert_called_once_with("1304.1068", expected)

    @respx.mock
    def test_s2_fallback_prefers_arxiv_over_pmc(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(200, json={"best_oa_location": None})
        )
        expected = tmp_path / "paper.pdf"
        mock_arxiv = MagicMock(return_value=expected)
        mock_pmc = MagicMock(return_value=expected)
        monkeypatch.setattr("huntpdf.resolvers.arxiv.resolve_arxiv", mock_arxiv)
        monkeypatch.setattr("huntpdf.resolvers.pmc.resolve_pmc", mock_pmc)
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi",
            MagicMock(return_value=ScholarResult(
                open_access_url="https://pmc.ncbi.nlm.nih.gov/articles/PMC4221854",
                arxiv_id="1304.1068",
                pmcid="PMC4221854",
            )),
        )

        result = resolve_doi(_SAMPLE_DOI, expected)
        assert result == expected
        mock_arxiv.assert_called_once()
        mock_pmc.assert_not_called()

    @respx.mock
    def test_s2_fallback_uses_pmcid(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(200, json={"best_oa_location": None})
        )
        expected = tmp_path / "paper.pdf"
        mock_pmc = MagicMock(return_value=expected)
        monkeypatch.setattr("huntpdf.resolvers.pmc.resolve_pmc", mock_pmc)
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi",
            MagicMock(return_value=ScholarResult(
                open_access_url="https://pmc.ncbi.nlm.nih.gov/articles/PMC4221854",
                arxiv_id=None,
                pmcid="PMC4221854",
            )),
        )

        result = resolve_doi(_SAMPLE_DOI, expected)
        assert result == expected
        mock_pmc.assert_called_once_with("PMC4221854", expected)

    @respx.mock
    def test_s2_returns_none_raises(self, monkeypatch):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(200, json={"best_oa_location": None})
        )
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi", MagicMock(return_value=None)
        )
        with pytest.raises(PDFNotFound, match="paywall"):
            resolve_doi(_SAMPLE_DOI)

    @respx.mock
    def test_unpaywall_404_falls_through_to_s2(self, monkeypatch):
        respx.get(_UNPAYWALL_URL).mock(return_value=Response(404))
        monkeypatch.setattr(
            "huntpdf.scholar.lookup_doi", MagicMock(return_value=None)
        )
        with pytest.raises(PDFNotFound, match="paywall"):
            resolve_doi(_SAMPLE_DOI)

    @respx.mock
    def test_default_filename(self, monkeypatch, tmp_path):
        respx.get(_UNPAYWALL_URL).mock(
            return_value=Response(
                200,
                json={
                    "best_oa_location": {
                        "url_for_pdf": "https://example.com/paper.pdf"
                    }
                },
            )
        )
        mock_download = MagicMock(return_value=tmp_path / "10.1038_s41586-020-2649-2.pdf")
        monkeypatch.setattr("huntpdf.resolvers.doi.download_pdf", mock_download)
        monkeypatch.chdir(tmp_path)

        resolve_doi(_SAMPLE_DOI)
        call_args = mock_download.call_args
        assert call_args[0][1].name == "10.1038_s41586-020-2649-2.pdf"

    def test_missing_email_raises(self, monkeypatch):
        monkeypatch.delenv("UNPAYWALL_EMAIL")
        with pytest.raises(PDFNotFound, match="UNPAYWALL_EMAIL"):
            resolve_doi(_SAMPLE_DOI)
