"""Tests for domain classifier."""

from unittest.mock import MagicMock, patch

from local_deep_research.domain_classifier.classifier import (
    DOMAIN_CATEGORIES,
    DomainClassifier,
)


class TestDomainCategories:
    """Tests for DOMAIN_CATEGORIES constant."""

    def test_has_expected_main_categories(self):
        """Should have all expected main categories."""
        expected = [
            "Academic & Research",
            "News & Media",
            "Reference & Documentation",
            "Social & Community",
            "Business & Commerce",
            "Technology",
            "Government & Organization",
            "Entertainment & Lifestyle",
            "Professional & Industry",
            "Other",
        ]
        for category in expected:
            assert category in DOMAIN_CATEGORIES

    def test_each_category_has_subcategories(self):
        """Should have subcategories for each main category."""
        for category, subcategories in DOMAIN_CATEGORIES.items():
            assert isinstance(subcategories, list)
            assert len(subcategories) > 0

    def test_other_includes_unknown(self):
        """Other category should include Unknown subcategory."""
        assert "Unknown" in DOMAIN_CATEGORIES["Other"]


class TestDomainClassifierInit:
    """Tests for DomainClassifier initialization."""

    def test_stores_username(self):
        """Should store username."""
        classifier = DomainClassifier(username="testuser")
        assert classifier.username == "testuser"

    def test_stores_settings_snapshot(self):
        """Should store settings snapshot."""
        snapshot = {"key": "value"}
        classifier = DomainClassifier(
            username="testuser", settings_snapshot=snapshot
        )
        assert classifier.settings_snapshot == snapshot

    def test_llm_initially_none(self):
        """Should have LLM as None initially."""
        classifier = DomainClassifier(username="testuser")
        assert classifier.llm is None


class TestGetLlm:
    """Tests for _get_llm method."""

    def test_creates_llm_on_first_call(self):
        """Should create LLM on first call."""
        mock_llm = MagicMock()
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_llm",
            return_value=mock_llm,
        ):
            result = classifier._get_llm()

        assert result is mock_llm
        assert classifier.llm is mock_llm

    def test_reuses_existing_llm(self):
        """Should reuse existing LLM on subsequent calls."""
        mock_llm = MagicMock()
        classifier = DomainClassifier(username="testuser")
        classifier.llm = mock_llm

        with patch(
            "local_deep_research.domain_classifier.classifier.get_llm"
        ) as mock_get:
            result = classifier._get_llm()

        mock_get.assert_not_called()
        assert result is mock_llm

    def test_passes_settings_snapshot(self):
        """Should pass settings snapshot to get_llm."""
        snapshot = {"key": "value"}
        classifier = DomainClassifier(
            username="testuser", settings_snapshot=snapshot
        )

        with patch(
            "local_deep_research.domain_classifier.classifier.get_llm"
        ) as mock_get:
            classifier._get_llm()

        mock_get.assert_called_once_with(settings_snapshot=snapshot)


class TestBuildClassificationPrompt:
    """Tests for _build_classification_prompt method."""

    def test_includes_domain(self):
        """Should include domain in prompt."""
        classifier = DomainClassifier(username="testuser")
        samples = []
        prompt = classifier._build_classification_prompt("example.com", samples)
        assert "example.com" in prompt

    def test_includes_categories(self, sample_samples):
        """Should include categories in prompt."""
        classifier = DomainClassifier(username="testuser")
        prompt = classifier._build_classification_prompt(
            "example.com", sample_samples
        )
        assert "News & Media" in prompt
        assert "Technology" in prompt

    def test_includes_samples(self, sample_samples):
        """Should include samples in prompt."""
        classifier = DomainClassifier(username="testuser")
        prompt = classifier._build_classification_prompt(
            "example.com", sample_samples
        )
        assert "Article 1" in prompt
        assert "Article 2" in prompt

    def test_handles_empty_samples(self):
        """Should handle empty samples."""
        classifier = DomainClassifier(username="testuser")
        prompt = classifier._build_classification_prompt("example.com", [])
        assert "No samples available" in prompt

    def test_requests_json_response(self, sample_samples):
        """Should request JSON response format."""
        classifier = DomainClassifier(username="testuser")
        prompt = classifier._build_classification_prompt(
            "example.com", sample_samples
        )
        assert "JSON" in prompt
        assert "category" in prompt
        assert "subcategory" in prompt
        assert "confidence" in prompt


class TestClassifyDomain:
    """Tests for classify_domain method."""

    def test_returns_existing_classification(self, mock_domain_classification):
        """Should return existing classification without reclassifying."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session = MagicMock()
            mock_session_ctx.return_value.__enter__ = MagicMock(
                return_value=mock_session
            )
            mock_session_ctx.return_value.__exit__ = MagicMock(
                return_value=None
            )
            mock_session.query.return_value.filter_by.return_value.first.return_value = mock_domain_classification

            result = classifier.classify_domain("example.com")

        assert result is mock_domain_classification

    def test_returns_none_on_error(self):
        """Should return None on error."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session_ctx.return_value.__enter__ = MagicMock(
                side_effect=Exception("DB error")
            )

            result = classifier.classify_domain("example.com")

        assert result is None


class TestGetClassification:
    """Tests for get_classification method."""

    def test_returns_classification_when_exists(
        self, mock_domain_classification
    ):
        """Should return classification when exists."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session = MagicMock()
            mock_session_ctx.return_value.__enter__ = MagicMock(
                return_value=mock_session
            )
            mock_session_ctx.return_value.__exit__ = MagicMock(
                return_value=None
            )
            mock_session.query.return_value.filter_by.return_value.first.return_value = mock_domain_classification

            result = classifier.get_classification("example.com")

        assert result is mock_domain_classification

    def test_returns_none_when_not_found(self):
        """Should return None when not found."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session = MagicMock()
            mock_session_ctx.return_value.__enter__ = MagicMock(
                return_value=mock_session
            )
            mock_session_ctx.return_value.__exit__ = MagicMock(
                return_value=None
            )
            mock_session.query.return_value.filter_by.return_value.first.return_value = None

            result = classifier.get_classification("unknown.com")

        assert result is None

    def test_returns_none_on_error(self):
        """Should return None on error."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session_ctx.return_value.__enter__ = MagicMock(
                side_effect=Exception("DB error")
            )

            result = classifier.get_classification("example.com")

        assert result is None


class TestGetAllClassifications:
    """Tests for get_all_classifications method."""

    def test_returns_list_of_classifications(self, mock_domain_classification):
        """Should return list of classifications."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session = MagicMock()
            mock_session_ctx.return_value.__enter__ = MagicMock(
                return_value=mock_session
            )
            mock_session_ctx.return_value.__exit__ = MagicMock(
                return_value=None
            )
            mock_session.query.return_value.order_by.return_value.all.return_value = [
                mock_domain_classification
            ]

            result = classifier.get_all_classifications()

        assert len(result) == 1
        assert result[0] is mock_domain_classification

    def test_returns_empty_list_on_error(self):
        """Should return empty list on error."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session_ctx.return_value.__enter__ = MagicMock(
                side_effect=Exception("DB error")
            )

            result = classifier.get_all_classifications()

        assert result == []


class TestGetCategoriesSummary:
    """Tests for get_categories_summary method."""

    def test_returns_summary_dict(self, mock_domain_classification):
        """Should return summary dictionary."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session = MagicMock()
            mock_session_ctx.return_value.__enter__ = MagicMock(
                return_value=mock_session
            )
            mock_session_ctx.return_value.__exit__ = MagicMock(
                return_value=None
            )
            mock_session.query.return_value.all.return_value = [
                mock_domain_classification
            ]

            result = classifier.get_categories_summary()

        assert "News & Media" in result
        assert result["News & Media"]["count"] == 1

    def test_returns_empty_dict_on_error(self):
        """Should return empty dict on error."""
        classifier = DomainClassifier(username="testuser")

        with patch(
            "local_deep_research.domain_classifier.classifier.get_user_db_session"
        ) as mock_session_ctx:
            mock_session_ctx.return_value.__enter__ = MagicMock(
                side_effect=Exception("DB error")
            )

            result = classifier.get_categories_summary()

        assert result == {}


class TestUrlparseValueErrorHandling:
    """Tests for ValueError handling in urlparse (PR #2012).

    PR #2012 changed bare `except:` to `except ValueError:` when
    parsing URLs in classify_all_domains.
    """

    def test_value_error_from_urlparse_caught(self):
        """ValueError from urlparse is caught with continue."""
        from urllib.parse import urlparse

        # URLs that could cause issues
        test_urls = ["https://valid.com/path", "", "not-a-url"]
        domains = set()

        for url in test_urls:
            if url:
                try:
                    parsed = urlparse(url)
                    domain = parsed.netloc.lower()
                    if domain.startswith("www."):
                        domain = domain[4:]
                    if domain:
                        domains.add(domain)
                except ValueError:
                    continue

        assert "valid.com" in domains
