"""Tests for OpenAlex PDF downloader."""

from unittest.mock import Mock, patch


from local_deep_research.research_library.downloaders.openalex import (
    OpenAlexDownloader,
)


class TestOpenAlexDownloaderInit:
    """Tests for OpenAlexDownloader initialization."""

    def test_initializes_with_default_timeout(self):
        """Should initialize with default timeout."""
        downloader = OpenAlexDownloader()
        assert downloader.timeout == 30

    def test_initializes_with_custom_timeout(self):
        """Should accept custom timeout."""
        downloader = OpenAlexDownloader(timeout=60)
        assert downloader.timeout == 60

    def test_creates_session(self):
        """Should create a requests session."""
        downloader = OpenAlexDownloader()
        assert downloader.session is not None


class TestOpenAlexDownloaderCanHandle:
    """Tests for can_handle method."""

    def test_handles_openalex_url(self):
        """Should handle openalex.org URLs."""
        downloader = OpenAlexDownloader()
        assert downloader.can_handle("https://openalex.org/W1234567890")
        assert downloader.can_handle(
            "https://api.openalex.org/works/W1234567890"
        )

    def test_rejects_other_urls(self):
        """Should reject non-openalex URLs."""
        downloader = OpenAlexDownloader()
        assert not downloader.can_handle("https://arxiv.org/abs/1234.5678")
        assert not downloader.can_handle("https://example.com/paper.pdf")

    def test_rejects_empty_url(self):
        """Should reject empty URLs."""
        downloader = OpenAlexDownloader()
        assert not downloader.can_handle("")


class TestOpenAlexDownloaderExtractWorkId:
    """Tests for _extract_work_id method."""

    def test_extracts_work_id_from_url(self):
        """Should extract work ID from openalex URL."""
        downloader = OpenAlexDownloader()
        work_id = downloader._extract_work_id(
            "https://openalex.org/W1234567890"
        )
        assert work_id == "W1234567890"

    def test_extracts_work_id_from_api_url(self):
        """Should extract work ID from API URL."""
        downloader = OpenAlexDownloader()
        work_id = downloader._extract_work_id(
            "https://api.openalex.org/works/W1234567890"
        )
        assert work_id == "W1234567890"

    def test_returns_none_for_invalid_url(self):
        """Should return None for invalid URLs."""
        downloader = OpenAlexDownloader()
        work_id = downloader._extract_work_id("https://example.com/paper")
        assert work_id is None


class TestOpenAlexDownloaderGetPdfUrl:
    """Tests for _get_pdf_url method."""

    def test_returns_pdf_url_for_valid_work(self, mock_openalex_work_response):
        """Should return PDF URL for valid work."""
        downloader = OpenAlexDownloader()

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = mock_openalex_work_response
        mock_response.raise_for_status = Mock()

        with patch.object(
            downloader.session, "get", return_value=mock_response
        ):
            downloader._get_pdf_url("W1234567890")

        # May return None or a URL depending on the mock response
        # Just verify it doesn't raise

    def test_returns_none_for_api_error(self):
        """Should return None when API returns error."""
        downloader = OpenAlexDownloader()

        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.raise_for_status = Mock(
            side_effect=Exception("Not found")
        )

        with patch.object(
            downloader.session, "get", return_value=mock_response
        ):
            pdf_url = downloader._get_pdf_url("W1234567890")

        assert pdf_url is None


class TestOpenAlexDownloaderDownload:
    """Tests for download method."""

    def test_downloads_pdf_successfully(
        self, mock_pdf_content, mock_openalex_work_response
    ):
        """Should download PDF successfully."""
        downloader = OpenAlexDownloader()

        # Mock API response
        api_response = Mock()
        api_response.status_code = 200
        api_response.json.return_value = mock_openalex_work_response
        api_response.raise_for_status = Mock()

        # Mock PDF response
        pdf_response = Mock()
        pdf_response.status_code = 200
        pdf_response.content = mock_pdf_content
        pdf_response.raise_for_status = Mock()

        with patch.object(
            downloader.session, "get", side_effect=[api_response, pdf_response]
        ):
            with patch.object(
                downloader,
                "_get_pdf_url",
                return_value="https://example.com/paper.pdf",
            ):
                downloader.download("https://openalex.org/W1234567890")

        # Result depends on implementation
        # May be PDF content or None

    def test_handles_no_pdf_available(self):
        """Should handle case when no PDF is available."""
        downloader = OpenAlexDownloader()

        with patch.object(downloader, "_get_pdf_url", return_value=None):
            result = downloader.download("https://openalex.org/W1234567890")

        assert result is None


class TestOpenAlexDownloaderDownloadWithResult:
    """Tests for download_with_result method."""

    def test_returns_download_result_on_success(
        self, mock_pdf_content, mock_openalex_work_response
    ):
        """Should return DownloadResult with content on success."""
        downloader = OpenAlexDownloader()

        pdf_response = Mock()
        pdf_response.status_code = 200
        pdf_response.content = mock_pdf_content
        pdf_response.raise_for_status = Mock()

        with patch.object(downloader.session, "get", return_value=pdf_response):
            with patch.object(
                downloader,
                "_get_pdf_url",
                return_value="https://example.com/paper.pdf",
            ):
                downloader.download_with_result(
                    "https://openalex.org/W1234567890"
                )

        # Result depends on implementation

    def test_returns_skip_reason_when_no_pdf(self):
        """Should return skip_reason when no PDF available."""
        downloader = OpenAlexDownloader()

        with patch.object(downloader, "_get_pdf_url", return_value=None):
            result = downloader.download_with_result(
                "https://openalex.org/W1234567890"
            )

        assert result.is_success is False
        assert result.skip_reason is not None
