"""
Extended tests for news/folder_manager.py

Tests cover:
- FolderManager initialization
- get_user_folders() method
- create_folder() method
- update_folder() method
- delete_folder() method
- get_subscriptions_by_folder() method
- update_subscription() method
- delete_subscription() method
- get_subscription_stats() method
- _sub_to_dict() helper
"""

import pytest
from unittest.mock import Mock, MagicMock, patch
from datetime import datetime, timezone, timedelta


@pytest.fixture
def mock_session():
    """Create a mock database session."""
    session = MagicMock()
    return session


class TestFolderManagerInit:
    """Tests for FolderManager initialization."""

    def test_stores_session(self, mock_session):
        """Stores the session."""
        from local_deep_research.news.folder_manager import FolderManager

        manager = FolderManager(mock_session)

        assert manager.session is mock_session

    def test_accepts_session_parameter(self, mock_session):
        """Accepts session parameter."""
        from local_deep_research.news.folder_manager import FolderManager

        manager = FolderManager(mock_session)

        assert manager is not None


class TestGetUserFolders:
    """Tests for get_user_folders() method."""

    def test_returns_list(self, mock_session):
        """Returns a list."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_user_folders("user1")

        assert isinstance(result, list)

    def test_orders_by_name(self, mock_session):
        """Orders folders by name."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        manager.get_user_folders("user1")

        mock_session.query.return_value.order_by.assert_called_once()

    def test_returns_all_folders(self, mock_session):
        """Returns all folders."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder1 = Mock()
        mock_folder2 = Mock()
        mock_session.query.return_value.order_by.return_value.all.return_value = [
            mock_folder1,
            mock_folder2,
        ]

        manager = FolderManager(mock_session)
        result = manager.get_user_folders("user1")

        assert len(result) == 2


class TestCreateFolder:
    """Tests for create_folder() method."""

    def test_creates_folder(self, mock_session):
        """Creates a folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.add = Mock()
        mock_session.commit = Mock()

        with patch(
            "local_deep_research.news.folder_manager.SubscriptionFolder"
        ) as MockFolder:
            mock_folder = Mock()
            MockFolder.return_value = mock_folder

            manager = FolderManager(mock_session)
            result = manager.create_folder("Test Folder")

            assert result is mock_folder
            mock_session.add.assert_called_once_with(mock_folder)
            mock_session.commit.assert_called_once()

    def test_creates_folder_with_description(self, mock_session):
        """Creates folder with description."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.add = Mock()
        mock_session.commit = Mock()

        with patch(
            "local_deep_research.news.folder_manager.SubscriptionFolder"
        ) as MockFolder:
            mock_folder = Mock()
            MockFolder.return_value = mock_folder

            manager = FolderManager(mock_session)
            manager.create_folder("Test Folder", description="A test folder")

            MockFolder.assert_called_once()
            call_kwargs = MockFolder.call_args[1]
            assert call_kwargs["description"] == "A test folder"

    def test_folder_gets_uuid_id(self, mock_session):
        """Folder gets UUID as ID."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.add = Mock()
        mock_session.commit = Mock()

        with patch(
            "local_deep_research.news.folder_manager.SubscriptionFolder"
        ) as MockFolder:
            mock_folder = Mock()
            MockFolder.return_value = mock_folder

            manager = FolderManager(mock_session)
            manager.create_folder("Test")

            call_kwargs = MockFolder.call_args[1]
            assert "id" in call_kwargs
            assert len(call_kwargs["id"]) == 36  # UUID length


class TestUpdateFolder:
    """Tests for update_folder() method."""

    def test_updates_existing_folder(self, mock_session):
        """Updates existing folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Old Name"
        mock_session.query.return_value.get.return_value = mock_folder
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        result = manager.update_folder(1, name="New Name")

        assert result is mock_folder
        assert mock_folder.name == "New Name"

    def test_returns_none_for_nonexistent(self, mock_session):
        """Returns None for nonexistent folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.get.return_value = None

        manager = FolderManager(mock_session)
        result = manager.update_folder(999, name="New Name")

        assert result is None

    def test_ignores_id_field(self, mock_session):
        """Ignores id field in updates."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.id = 1
        mock_session.query.return_value.get.return_value = mock_folder
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.update_folder(1, id=999)

        # ID should not change
        assert mock_folder.id == 1

    def test_ignores_created_at_field(self, mock_session):
        """Ignores created_at field in updates."""
        from local_deep_research.news.folder_manager import FolderManager

        original_created = datetime.now(timezone.utc)
        mock_folder = Mock()
        mock_folder.created_at = original_created
        mock_session.query.return_value.get.return_value = mock_folder
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        new_created = datetime.now(timezone.utc) - timedelta(days=1)
        manager.update_folder(1, created_at=new_created)

        # created_at should not change
        assert mock_folder.created_at == original_created

    def test_updates_updated_at(self, mock_session):
        """Updates updated_at timestamp."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.updated_at = None
        mock_session.query.return_value.get.return_value = mock_folder
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.update_folder(1, name="New")

        assert mock_folder.updated_at is not None


class TestDeleteFolder:
    """Tests for delete_folder() method."""

    def test_deletes_existing_folder(self, mock_session):
        """Deletes existing folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Test"
        mock_session.query.return_value.get.return_value = mock_folder
        mock_session.query.return_value.filter_by.return_value.update = Mock()
        mock_session.delete = Mock()
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        result = manager.delete_folder(1)

        assert result is True
        mock_session.delete.assert_called_once_with(mock_folder)

    def test_returns_false_for_nonexistent(self, mock_session):
        """Returns False for nonexistent folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.get.return_value = None

        manager = FolderManager(mock_session)
        result = manager.delete_folder(999)

        assert result is False

    def test_moves_subscriptions_when_specified(self, mock_session):
        """Moves subscriptions to target folder when specified."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Old Folder"
        mock_session.query.return_value.get.return_value = mock_folder
        mock_update = Mock()
        mock_session.query.return_value.filter_by.return_value.update = (
            mock_update
        )
        mock_session.delete = Mock()
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.delete_folder(1, move_to="New Folder")

        mock_update.assert_called_once_with({"folder": "New Folder"})

    def test_sets_folder_to_none_when_no_target(self, mock_session):
        """Sets subscription folder to None when no target."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Test"
        mock_session.query.return_value.get.return_value = mock_folder
        mock_update = Mock()
        mock_session.query.return_value.filter_by.return_value.update = (
            mock_update
        )
        mock_session.delete = Mock()
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.delete_folder(1)

        mock_update.assert_called_once_with({"folder": None})


class TestGetSubscriptionsByFolder:
    """Tests for get_subscriptions_by_folder() method."""

    def test_returns_dict(self, mock_session):
        """Returns a dictionary."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.order_by.return_value.all.return_value = []
        mock_session.query.return_value.filter_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscriptions_by_folder("user1")

        assert isinstance(result, dict)

    def test_has_folders_key(self, mock_session):
        """Result has folders key."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.order_by.return_value.all.return_value = []
        mock_session.query.return_value.filter_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscriptions_by_folder("user1")

        assert "folders" in result

    def test_has_uncategorized_key(self, mock_session):
        """Result has uncategorized key."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.order_by.return_value.all.return_value = []
        mock_session.query.return_value.filter_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscriptions_by_folder("user1")

        assert "uncategorized" in result


class TestUpdateSubscription:
    """Tests for update_subscription() method."""

    def test_updates_existing_subscription(self, mock_session):
        """Updates existing subscription."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.name = "Old Name"
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        result = manager.update_subscription("sub-1", name="New Name")

        assert result is mock_sub
        assert mock_sub.name == "New Name"

    def test_returns_none_for_nonexistent(self, mock_session):
        """Returns None for nonexistent subscription."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.filter_by.return_value.first.return_value = None

        manager = FolderManager(mock_session)
        result = manager.update_subscription("nonexistent", name="New")

        assert result is None

    def test_recalculates_next_refresh_when_interval_changes(
        self, mock_session
    ):
        """Recalculates next_refresh when refresh_interval_minutes changes."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.last_refresh = datetime.now(timezone.utc) - timedelta(hours=1)
        mock_sub.next_refresh = datetime.now(timezone.utc)
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-1", refresh_interval_minutes=120)

        # next_refresh should be recalculated
        assert mock_sub.next_refresh is not None

    def test_updates_updated_at_timestamp(self, mock_session):
        """Updates updated_at timestamp."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.updated_at = None
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-1", name="New")

        assert mock_sub.updated_at is not None


class TestDeleteSubscription:
    """Tests for delete_subscription() method."""

    def test_deletes_existing_subscription(self, mock_session):
        """Deletes existing subscription."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_session.query.return_value.filter_by.return_value.first.return_value = mock_sub
        mock_session.delete = Mock()
        mock_session.commit = Mock()

        manager = FolderManager(mock_session)
        result = manager.delete_subscription("sub-1")

        assert result is True
        mock_session.delete.assert_called_once_with(mock_sub)

    def test_returns_false_for_nonexistent(self, mock_session):
        """Returns False for nonexistent subscription."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.filter_by.return_value.first.return_value = None

        manager = FolderManager(mock_session)
        result = manager.delete_subscription("nonexistent")

        assert result is False


class TestGetSubscriptionStats:
    """Tests for get_subscription_stats() method."""

    def test_returns_dict(self, mock_session):
        """Returns a dictionary."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.count.return_value = 0
        mock_session.query.return_value.filter_by.return_value.count.return_value = 0
        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscription_stats("user1")

        assert isinstance(result, dict)

    def test_has_total_key(self, mock_session):
        """Result has total key."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.count.return_value = 5
        mock_session.query.return_value.filter_by.return_value.count.return_value = 0
        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscription_stats("user1")

        assert result["total"] == 5

    def test_has_active_key(self, mock_session):
        """Result has active key."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.count.return_value = 0
        mock_session.query.return_value.filter_by.return_value.count.return_value = 3
        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscription_stats("user1")

        assert "active" in result

    def test_has_by_type_key(self, mock_session):
        """Result has by_type key."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.count.return_value = 0
        mock_session.query.return_value.filter_by.return_value.count.return_value = 0
        mock_session.query.return_value.order_by.return_value.all.return_value = []

        manager = FolderManager(mock_session)
        result = manager.get_subscription_stats("user1")

        assert "by_type" in result

    def test_has_folders_count(self, mock_session):
        """Result has folders count."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session.query.return_value.count.return_value = 0
        mock_session.query.return_value.filter_by.return_value.count.return_value = 0
        mock_folder = Mock()
        mock_session.query.return_value.order_by.return_value.all.return_value = [
            mock_folder
        ]

        manager = FolderManager(mock_session)
        result = manager.get_subscription_stats("user1")

        assert "folders" in result
        assert result["folders"] == 1


class TestSubToDict:
    """Tests for _sub_to_dict() helper."""

    def test_returns_dict(self, mock_session):
        """Returns a dictionary."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.id = "sub-1"
        mock_sub.subscription_type = "search"
        mock_sub.query_or_topic = "test query"
        mock_sub.created_at = None
        mock_sub.last_refresh = None
        mock_sub.next_refresh = None
        mock_sub.refresh_interval_minutes = 60
        mock_sub.status = "active"

        manager = FolderManager(mock_session)
        result = manager._sub_to_dict(mock_sub)

        assert isinstance(result, dict)

    def test_includes_id(self, mock_session):
        """Result includes id."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.id = "sub-123"
        mock_sub.subscription_type = "search"
        mock_sub.query_or_topic = "test"
        mock_sub.created_at = None
        mock_sub.last_refresh = None
        mock_sub.next_refresh = None
        mock_sub.refresh_interval_minutes = 60
        mock_sub.status = "active"

        manager = FolderManager(mock_session)
        result = manager._sub_to_dict(mock_sub)

        assert result["id"] == "sub-123"

    def test_formats_dates_as_iso(self, mock_session):
        """Formats dates as ISO strings."""
        from local_deep_research.news.folder_manager import FolderManager

        now = datetime.now(timezone.utc)
        mock_sub = Mock()
        mock_sub.id = "sub-1"
        mock_sub.subscription_type = "search"
        mock_sub.query_or_topic = "test"
        mock_sub.created_at = now
        mock_sub.last_refresh = now
        mock_sub.next_refresh = now
        mock_sub.refresh_interval_minutes = 60
        mock_sub.status = "active"

        manager = FolderManager(mock_session)
        result = manager._sub_to_dict(mock_sub)

        assert result["created_at"] == now.isoformat()

    def test_handles_none_dates(self, mock_session):
        """Handles None dates."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.id = "sub-1"
        mock_sub.subscription_type = "search"
        mock_sub.query_or_topic = "test"
        mock_sub.created_at = None
        mock_sub.last_refresh = None
        mock_sub.next_refresh = None
        mock_sub.refresh_interval_minutes = 60
        mock_sub.status = "active"

        manager = FolderManager(mock_session)
        result = manager._sub_to_dict(mock_sub)

        assert result["created_at"] is None
        assert result["last_refresh"] is None
        assert result["next_refresh"] is None
