"""
Tests for uncovered code paths in GenericDownloader.

Targets:
- download_with_result: diagnostic HTTP status code handling (200 HTML, 404, 403, 401, 500+, other)
- download_with_result: timeout, connection error, generic request error
- download_with_result: PDF content type with text extraction
- download_with_result: .pdf URL append fallback
- _download_pdf: URL with .pdf extension already (skips append)
"""

from unittest.mock import Mock, patch

import pytest
import requests

from local_deep_research.research_library.downloaders.generic import (
    GenericDownloader,
)
from local_deep_research.research_library.downloaders.base import (
    ContentType,
)

MODULE = "local_deep_research.research_library.downloaders.generic"


@pytest.fixture
def downloader():
    dl = GenericDownloader(timeout=30)
    return dl


class TestDownloadWithResultDiagnostic:
    """Tests for download_with_result diagnostic HTTP status branches."""

    def test_200_html_returns_login_required(self, downloader):
        """200 with HTML content type suggests login/subscription needed."""
        # Mock both _download_pdf calls to fail
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            # Mock diagnostic request
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.headers = {"content-type": "text/html; charset=utf-8"}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "login or subscription" in result.skip_reason

    def test_200_unexpected_content_type(self, downloader):
        """200 with non-HTML non-PDF content type."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 200
            mock_response.headers = {"content-type": "application/xml"}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Unexpected content type" in result.skip_reason

    def test_404_returns_not_found(self, downloader):
        """404 produces correct skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 404
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "not found (404)" in result.skip_reason

    def test_403_returns_access_denied(self, downloader):
        """403 produces access denied skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 403
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Access denied (403)" in result.skip_reason

    def test_401_returns_auth_required(self, downloader):
        """401 produces authentication required skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 401
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Authentication required" in result.skip_reason

    def test_500_returns_server_error(self, downloader):
        """500+ produces server error skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 503
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Server error (503)" in result.skip_reason

    def test_other_status_code(self, downloader):
        """Other status codes produce generic error."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 451  # Unavailable For Legal Reasons
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "451" in result.skip_reason


class TestDownloadWithResultNetworkErrors:
    """Tests for network error handling in download_with_result."""

    def test_timeout_error(self, downloader):
        """Timeout produces correct skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            downloader.session = Mock()
            downloader.session.get.side_effect = requests.exceptions.Timeout()

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "timed out" in result.skip_reason

    def test_connection_error(self, downloader):
        """Connection error produces correct skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            downloader.session = Mock()
            downloader.session.get.side_effect = (
                requests.exceptions.ConnectionError()
            )

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Could not connect" in result.skip_reason

    def test_generic_request_error(self, downloader):
        """Generic request exception produces network error skip reason."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            downloader.session = Mock()
            downloader.session.get.side_effect = requests.RequestException(
                "weird error"
            )

            result = downloader.download_with_result(
                "https://example.com/paper"
            )

            assert result.is_success is False
            assert "Network error" in result.skip_reason


class TestDownloadWithResultTextContent:
    """Tests for text content extraction in download_with_result."""

    def test_text_content_success(self, downloader):
        """Text content type downloads PDF and extracts text."""
        with patch.object(
            downloader, "_download_pdf", return_value=b"fake pdf"
        ):
            with patch.object(
                downloader,
                "extract_text_from_pdf",
                return_value="Extracted text content",
            ):
                result = downloader.download_with_result(
                    "https://example.com/paper", ContentType.TEXT
                )

        assert result.is_success is True
        assert result.content == b"Extracted text content"

    def test_text_content_extraction_fails(self, downloader):
        """Text extraction fails after PDF download."""
        with patch.object(
            downloader, "_download_pdf", return_value=b"fake pdf"
        ):
            with patch.object(
                downloader, "extract_text_from_pdf", return_value=None
            ):
                result = downloader.download_with_result(
                    "https://example.com/paper", ContentType.TEXT
                )

        assert result.is_success is False
        assert "text extraction failed" in result.skip_reason

    def test_text_content_pdf_download_fails(self, downloader):
        """PDF download fails for text content type."""
        with patch.object(downloader, "_download_pdf", return_value=None):
            result = downloader.download_with_result(
                "https://example.com/paper", ContentType.TEXT
            )

        assert result.is_success is False
        assert "Could not download PDF" in result.skip_reason


class TestDownloadWithResultPdfFallback:
    """Tests for .pdf URL append fallback in download_with_result."""

    def test_pdf_url_append_succeeds(self, downloader):
        """Appending .pdf to URL succeeds when direct download fails."""
        call_count = [0]

        def mock_download(url, *args, **kwargs):
            call_count[0] += 1
            if call_count[0] == 1:
                return None  # Direct download fails
            return b"%PDF-1.4 fake pdf"  # .pdf appended URL succeeds

        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            side_effect=mock_download,
        ):
            result = downloader.download_with_result(
                "https://example.com/paper"
            )

        assert result.is_success is True

    def test_pdf_url_already_has_extension(self, downloader):
        """URL already ending in .pdf doesn't get .pdf appended."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            mock_response = Mock()
            mock_response.status_code = 404
            mock_response.headers = {}
            mock_response.__enter__ = Mock(return_value=mock_response)
            mock_response.__exit__ = Mock(return_value=False)

            downloader.session = Mock()
            downloader.session.get.return_value = mock_response

            result = downloader.download_with_result(
                "https://example.com/paper.pdf"
            )

        assert result.is_success is False
        # Should NOT try appending .pdf again


class TestDownloadPdf:
    """Tests for _download_pdf method."""

    def test_direct_download_success(self, downloader):
        """Direct download succeeds."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=b"%PDF",
        ):
            result = downloader._download_pdf("https://example.com/paper.pdf")
        assert result == b"%PDF"

    def test_appends_pdf_extension(self, downloader):
        """Appends .pdf to URL when direct download fails."""
        call_count = [0]

        def mock_download(url, *args, **kwargs):
            call_count[0] += 1
            if ".pdf" in url and call_count[0] > 1:
                return b"%PDF"
            return None

        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            side_effect=mock_download,
        ):
            result = downloader._download_pdf("https://example.com/paper")

        assert result == b"%PDF"

    def test_both_downloads_fail(self, downloader):
        """Returns None when both downloads fail."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            result = downloader._download_pdf("https://example.com/paper")
        assert result is None

    def test_url_already_has_pdf_extension(self, downloader):
        """URL with .pdf extension doesn't try double append."""
        with patch.object(
            downloader.__class__.__bases__[0],
            "_download_pdf",
            return_value=None,
        ):
            result = downloader._download_pdf("https://example.com/paper.pdf")
        assert result is None
