"""
Tests for ResourceFilter: should_skip_resource(), get_skipped_resources_info(),
and get_retry_statistics() in filters/resource_filter.py.

All tests mock the retry_manager to isolate the filtering logic.
"""

from datetime import timedelta
from unittest.mock import MagicMock, patch

from local_deep_research.library.download_management.retry_manager import (
    RetryDecision,
    ResourceFilterResult,
)


def _make_filter_with_mock_retry():
    """Create a ResourceFilter with a mocked retry_manager."""
    with patch(
        "local_deep_research.library.download_management.filters.resource_filter.RetryManager"
    ):
        from local_deep_research.library.download_management.filters.resource_filter import (
            ResourceFilter,
        )

        rf = ResourceFilter.__new__(ResourceFilter)
        rf.username = "test_user"
        rf.retry_manager = MagicMock()
        return rf


class TestShouldSkipResource:
    """Tests for should_skip_resource()."""

    def test_can_retry_returns_no_skip(self):
        """When retry_manager says can_retry=True, should_skip is False."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.should_retry_resource.return_value = RetryDecision(
            can_retry=True, reason="OK"
        )
        skip, reason = rf.should_skip_resource(42)
        assert skip is False

    def test_cannot_retry_returns_skip(self):
        """When can_retry=False, should_skip is True with reason."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.should_retry_resource.return_value = RetryDecision(
            can_retry=False, reason="Permanently failed"
        )
        skip, reason = rf.should_skip_resource(42)
        assert skip is True
        assert reason == "Permanently failed"

    def test_none_reason_gives_default(self):
        """When reason is None, a default reason string is provided."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.should_retry_resource.return_value = RetryDecision(
            can_retry=False, reason=None
        )
        skip, reason = rf.should_skip_resource(99)
        assert skip is True
        assert "not available" in reason.lower()

    def test_delegates_to_retry_manager(self):
        """Verify the resource_id is passed to retry_manager."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.should_retry_resource.return_value = RetryDecision(
            can_retry=True
        )
        rf.should_skip_resource(123)
        rf.retry_manager.should_retry_resource.assert_called_once_with(123)


class TestGetSkippedResourcesInfo:
    """Tests for get_skipped_resources_info()."""

    def _setup_filter(self, filter_results):
        """Set up filter with mocked filter_downloadable_resources."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.filter_resources.return_value = filter_results
        rf.retry_manager.status_tracker = MagicMock()
        rf.retry_manager.status_tracker.get_resource_status.return_value = {
            "attempts": 3
        }
        return rf

    def test_empty_resources(self):
        """No resources → all counts zero."""
        rf = self._setup_filter([])
        result = rf.get_skipped_resources_info([])
        assert result["total_skipped"] == 0
        assert result["permanently_failed"] == []
        assert result["temporarily_failed"] == []
        assert result["other_skipped"] == []
        assert result["skipped_resources"] == []

    def test_all_can_retry(self):
        """All resources can retry → nothing skipped."""
        results = [
            ResourceFilterResult(1, can_retry=True, status="available"),
            ResourceFilterResult(2, can_retry=True, status="available"),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r1", "r2"])
        assert info["total_skipped"] == 0

    def test_permanently_failed_categorization(self):
        """Permanently failed resources go to permanently_failed list."""
        results = [
            ResourceFilterResult(
                1,
                can_retry=False,
                status="permanently_failed",
                reason="Too many failures",
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r1"])
        assert info["total_skipped"] == 1
        assert len(info["permanently_failed"]) == 1
        assert info["permanently_failed"][0]["resource_id"] == 1

    def test_temporarily_failed_categorization(self):
        """Temporarily failed resources go to temporarily_failed list."""
        results = [
            ResourceFilterResult(
                2,
                can_retry=False,
                status="temporarily_failed",
                reason="Cooling down",
                estimated_wait=timedelta(minutes=5),
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r2"])
        assert len(info["temporarily_failed"]) == 1
        assert info["temporarily_failed"][0]["estimated_wait_minutes"] == 5.0

    def test_other_skipped_categorization(self):
        """Non-standard statuses go to other_skipped."""
        results = [
            ResourceFilterResult(
                3, can_retry=False, status="unknown_status", reason="Unknown"
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r3"])
        assert len(info["other_skipped"]) == 1
        assert info["other_skipped"][0]["status"] == "unknown_status"

    def test_mixed_statuses(self):
        """Mix of permanently_failed, temporarily_failed, other, and retryable."""
        results = [
            ResourceFilterResult(1, can_retry=True, status="available"),
            ResourceFilterResult(
                2, can_retry=False, status="permanently_failed", reason="Dead"
            ),
            ResourceFilterResult(
                3, can_retry=False, status="temporarily_failed", reason="Wait"
            ),
            ResourceFilterResult(
                4, can_retry=False, status="rate_limited", reason="Slow down"
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r1", "r2", "r3", "r4"])
        assert info["total_skipped"] == 3
        assert len(info["permanently_failed"]) == 1
        assert len(info["temporarily_failed"]) == 1
        assert len(info["other_skipped"]) == 1

    def test_estimated_wait_none(self):
        """When estimated_wait is None, estimated_wait_minutes is None."""
        results = [
            ResourceFilterResult(
                5,
                can_retry=False,
                status="permanently_failed",
                reason="Failed",
                estimated_wait=None,
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r5"])
        assert info["permanently_failed"][0]["estimated_wait_minutes"] is None

    def test_estimated_wait_timedelta(self):
        """Timedelta estimated_wait → minutes conversion."""
        results = [
            ResourceFilterResult(
                6,
                can_retry=False,
                status="temporarily_failed",
                reason="Cooldown",
                estimated_wait=timedelta(minutes=10),
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r6"])
        assert info["temporarily_failed"][0]["estimated_wait_minutes"] == 10.0

    def test_status_info_included(self):
        """Status info from status_tracker is included."""
        results = [
            ResourceFilterResult(
                7, can_retry=False, status="permanently_failed", reason="Done"
            ),
        ]
        rf = self._setup_filter(results)
        rf.retry_manager.status_tracker.get_resource_status.return_value = {
            "attempts": 5,
            "last_failure": "timeout",
        }
        info = rf.get_skipped_resources_info(["r7"])
        assert info["permanently_failed"][0]["status_info"]["attempts"] == 5

    def test_skipped_resources_list_matches_total(self):
        """The skipped_resources flat list has all skipped entries."""
        results = [
            ResourceFilterResult(
                1, can_retry=False, status="permanently_failed", reason="A"
            ),
            ResourceFilterResult(
                2, can_retry=False, status="temporarily_failed", reason="B"
            ),
        ]
        rf = self._setup_filter(results)
        info = rf.get_skipped_resources_info(["r1", "r2"])
        assert len(info["skipped_resources"]) == info["total_skipped"] == 2


class TestGetRetryStatistics:
    """Tests for get_retry_statistics()."""

    def test_delegates_to_retry_manager(self):
        """Delegates directly to retry_manager.get_retry_statistics()."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.get_retry_statistics.return_value = {"total": 10}
        result = rf.get_retry_statistics()
        assert result == {"total": 10}
        rf.retry_manager.get_retry_statistics.assert_called_once()

    def test_returns_dict(self):
        """Return type is dict."""
        rf = _make_filter_with_mock_retry()
        rf.retry_manager.get_retry_statistics.return_value = {}
        assert isinstance(rf.get_retry_statistics(), dict)
