"""Tests for external link reachability check."""
from unittest.mock import MagicMock, patch

import pytest
import requests

from scanner.checker import check_external_links


def _mock_response(status_code=200, url="https://example.com/"):
    resp = MagicMock()
    resp.status_code = status_code
    resp.url = url
    return resp


class TestCheckExternalLinks:
    def test_reachable_link_recorded(self):
        with patch("scanner.checker.requests.Session") as MockSession:
            session = MockSession.return_value.__enter__.return_value
            MockSession.return_value = MagicMock()
            inst = MockSession.return_value
            inst.head.return_value = _mock_response(200)
            inst.close = MagicMock()

            results = check_external_links(["https://example.com/"], delay=0)

        assert results["https://example.com/"]["status"] == 200
        assert results["https://example.com/"]["error"] is None

    def test_broken_link_404_recorded(self):
        with patch("scanner.checker.requests.Session") as MockSession:
            inst = MockSession.return_value
            inst.head.return_value = _mock_response(404, "https://example.com/gone")
            inst.close = MagicMock()

            results = check_external_links(["https://example.com/gone"], delay=0)

        assert results["https://example.com/gone"]["status"] == 404

    def test_connection_error_recorded(self):
        with patch("scanner.checker.requests.Session") as MockSession:
            inst = MockSession.return_value
            inst.head.side_effect = requests.exceptions.ConnectionError("refused")
            inst.close = MagicMock()

            results = check_external_links(["https://unreachable.example.com/"], delay=0)

        r = results["https://unreachable.example.com/"]
        assert r["status"] is None
        assert "refused" in r["error"]

    def test_head_405_falls_back_to_get(self):
        with patch("scanner.checker.requests.Session") as MockSession:
            inst = MockSession.return_value
            inst.head.return_value = _mock_response(405)
            get_resp = _mock_response(200)
            get_resp.close = MagicMock()
            inst.get.return_value = get_resp
            inst.close = MagicMock()

            results = check_external_links(["https://example.com/no-head"], delay=0)

        inst.get.assert_called_once()
        assert results["https://example.com/no-head"]["status"] == 200

    def test_multiple_urls_all_checked(self):
        urls = [f"https://example.com/{i}" for i in range(3)]
        with patch("scanner.checker.requests.Session") as MockSession:
            inst = MockSession.return_value
            inst.head.return_value = _mock_response(200)
            inst.close = MagicMock()

            results = check_external_links(urls, delay=0)

        assert len(results) == 3
        assert inst.head.call_count == 3

    def test_403_retries_with_browser_ua_and_succeeds(self):
        """Bot-blocking 403 → retry with browser UA → 200 = link is OK."""
        with patch("scanner.checker.requests.Session") as MockSession:
            inst = MockSession.return_value
            ok_resp = _mock_response(200, "https://example.com/")
            inst.head.side_effect = [_mock_response(403), ok_resp]
            inst.close = MagicMock()

            results = check_external_links(["https://example.com/"], delay=0)

        assert results["https://example.com/"]["status"] == 200
        assert inst.head.call_count == 2

    def test_empty_list_returns_empty(self):
        with patch("scanner.checker.requests.Session"):
            results = check_external_links([], delay=0)
        assert results == {}
