"""Tests for library/download_management/status_tracker.py."""

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


class TestResourceStatusTrackerInit:
    """Tests for ResourceStatusTracker initialization."""

    def test_stores_username(self):
        """Test that username is stored."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                assert tracker.username == "testuser"

    def test_stores_password(self):
        """Test that password is stored."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "mypass")

                assert tracker.password == "mypass"

    def test_opens_user_database(self):
        """Test that user database is opened."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                ResourceStatusTracker("testuser", "testpass")

                mock_db.open_user_database.assert_called_once_with(
                    "testuser", "testpass"
                )

    def test_creates_tables(self):
        """Test that tables are created."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ) as mock_base:
                ResourceStatusTracker("testuser", "testpass")

                mock_base.metadata.create_all.assert_called_once_with(
                    mock_engine
                )


class TestMarkFailure:
    """Tests for mark_failure method."""

    def test_marks_permanent_failure_correctly(self):
        """Test that permanent failures are marked correctly."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from local_deep_research.library.download_management.failure_classifier import (
            PermanentFailure,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                # Create mock session and status
                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.total_retry_count = 0
                mock_status.today_retry_count = 0
                mock_status.last_attempt_at = None
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                failure = PermanentFailure("not_found", "Resource not found")

                tracker.mark_failure(123, failure, session=mock_session)

                assert mock_status.status == "permanently_failed"
                assert mock_status.failure_type == "not_found"

    def test_marks_temporary_failure_correctly(self):
        """Test that temporary failures are marked correctly."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from local_deep_research.library.download_management.failure_classifier import (
            TemporaryFailure,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.total_retry_count = 0
                mock_status.today_retry_count = 0
                mock_status.last_attempt_at = None
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                failure = TemporaryFailure(
                    "timeout", "Request timed out", timedelta(hours=1)
                )

                tracker.mark_failure(456, failure, session=mock_session)

                assert mock_status.status == "temporarily_failed"
                assert mock_status.failure_type == "timeout"

    def test_increments_retry_count(self):
        """Test that retry count is incremented."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from local_deep_research.library.download_management.failure_classifier import (
            TemporaryFailure,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.total_retry_count = 5
                mock_status.today_retry_count = 2
                mock_status.last_attempt_at = datetime.now(UTC)
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                failure = TemporaryFailure("error", "Error", timedelta(hours=1))

                tracker.mark_failure(789, failure, session=mock_session)

                assert mock_status.total_retry_count == 6


class TestMarkSuccess:
    """Tests for mark_success method."""

    def test_marks_status_as_completed(self):
        """Test that status is marked as completed."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.status = "temporarily_failed"
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                tracker.mark_success(123, session=mock_session)

                assert mock_status.status == "completed"
                assert mock_status.failure_type is None
                assert mock_status.failure_message is None

    def test_handles_no_existing_status(self):
        """Test that mark_success handles case with no existing status."""
        # audit: PUNCHLIST reviewed 2026-05 — issue resolved by prior PR (recommendation: strengthen — assert mock_session.add not called).
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_session.query.return_value.filter_by.return_value.first.return_value = None

                # Should not raise an error
                tracker.mark_success(123, session=mock_session)


class TestCanRetry:
    """Tests for can_retry method."""

    def test_returns_true_for_no_status(self):
        """Test that True is returned when no status record exists."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from contextlib import contextmanager

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_session.query.return_value.filter_by.return_value.first.return_value = None

                @contextmanager
                def mock_get_session():
                    yield mock_session

                tracker._get_session = mock_get_session

                can_retry, reason = tracker.can_retry(123)

                assert can_retry is True
                assert reason is None

    def test_returns_false_for_permanent_failure(self):
        """Test that False is returned for permanent failures."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from contextlib import contextmanager

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.status = "permanently_failed"
                mock_status.failure_type = "not_found"
                mock_status.failure_message = "Resource not found"
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                @contextmanager
                def mock_get_session():
                    yield mock_session

                tracker._get_session = mock_get_session

                can_retry, reason = tracker.can_retry(123)

                assert can_retry is False
                assert "Permanently failed" in reason

    def test_returns_false_when_daily_limit_exceeded(self):
        """Test that False is returned when daily retry limit exceeded."""
        from local_deep_research.library.download_management.status_tracker import (
            ResourceStatusTracker,
        )
        from contextlib import contextmanager

        with patch(
            "local_deep_research.database.encrypted_db.db_manager"
        ) as mock_db:
            mock_engine = Mock()
            mock_db.open_user_database.return_value = mock_engine

            with patch(
                "local_deep_research.library.download_management.status_tracker.Base"
            ):
                tracker = ResourceStatusTracker("testuser", "testpass")

                mock_session = MagicMock()
                mock_status = Mock()
                mock_status.status = "temporarily_failed"
                mock_status.retry_after_timestamp = None
                mock_status.today_retry_count = 5  # Exceeds limit of 3
                mock_status.last_attempt_at = datetime.now(
                    UTC
                )  # Must be today for daily check
                mock_session.query.return_value.filter_by.return_value.first.return_value = mock_status

                @contextmanager
                def mock_get_session():
                    yield mock_session

                tracker._get_session = mock_get_session

                can_retry, reason = tracker.can_retry(123)

                assert can_retry is False
                assert "Daily retry limit" in reason
