"""
Comprehensive tests for FolderManager class.
Tests CRUD operations for folders and subscription organization.
"""

from unittest.mock import Mock
from datetime import datetime, timezone, timedelta
import uuid


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

    def test_accepts_session(self):
        """Test accepts session parameter."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)
        assert manager.session == mock_session

    def test_stores_session_reference(self):
        """Test stores session reference."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)
        assert manager.session is mock_session


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

    def test_returns_list(self):
        """Test returns a list."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert isinstance(result, list)

    def test_orders_by_name(self):
        """Test orders folders by name."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)
        manager.get_user_folders("user123")

        # Verify order_by was called
        mock_session.query().order_by.assert_called()

    def test_returns_all_folders(self):
        """Test returns all folders from query."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert len(result) == 2


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

    def test_creates_folder_with_name(self):
        """Test creates folder with given name."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        manager.create_folder("My Folder")

        # Verify add was called
        mock_session.add.assert_called_once()
        added_folder = mock_session.add.call_args[0][0]
        assert added_folder.name == "My Folder"

    def test_creates_folder_with_description(self):
        """Test creates folder with description."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        manager.create_folder("My Folder", description="Test desc")

        added_folder = mock_session.add.call_args[0][0]
        assert added_folder.description == "Test desc"

    def test_generates_uuid_for_id(self):
        """Test generates UUID for folder ID."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        manager.create_folder("Test")

        added_folder = mock_session.add.call_args[0][0]
        # Should be a valid UUID
        uuid.UUID(added_folder.id)

    def test_commits_session(self):
        """Test commits session after creating folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        manager.create_folder("Test")

        mock_session.commit.assert_called_once()

    def test_returns_created_folder(self):
        """Test returns the created folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        result = manager.create_folder("Test", description="Desc")

        assert result.name == "Test"
        assert result.description == "Desc"


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

    def test_returns_none_for_nonexistent_folder(self):
        """Test returns None when folder not found."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        mock_session.query().get.return_value = None

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

        assert result is None

    def test_updates_name(self):
        """Test updates folder name."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert mock_folder.name == "New Name"

    def test_updates_description(self):
        """Test updates folder description."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.description = "Old desc"
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        assert mock_folder.description == "New desc"

    def test_does_not_update_id(self):
        """Test does not update folder ID."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.id = "original-id"
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

        manager = FolderManager(mock_session)
        manager.update_folder(1, id="new-id")

        # ID should not change
        assert mock_folder.id == "original-id"

    def test_does_not_update_created_at(self):
        """Test does not update created_at."""
        from local_deep_research.news.folder_manager import FolderManager

        original_time = datetime(2023, 1, 1, tzinfo=timezone.utc)
        mock_folder = Mock()
        mock_folder.created_at = original_time
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

        manager = FolderManager(mock_session)
        manager.update_folder(1, created_at=datetime.now(timezone.utc))

        assert mock_folder.created_at == original_time

    def test_updates_updated_at(self):
        """Test updates updated_at timestamp."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        # updated_at should be set
        assert mock_folder.updated_at is not None

    def test_commits_session(self):
        """Test commits session after update."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        mock_session.commit.assert_called_once()

    def test_returns_updated_folder(self):
        """Test returns the updated folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        assert result is mock_folder


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

    def test_returns_false_for_nonexistent_folder(self):
        """Test returns False when folder not found."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        mock_session.query().get.return_value = None

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

        assert result is False

    def test_moves_subscriptions_to_target_folder(self):
        """Test moves subscriptions to target folder."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        # Should update subscriptions
        mock_session.query().filter_by().update.assert_called_with(
            {"folder": "New Folder"}
        )

    def test_clears_folder_on_subscriptions_when_no_target(self):
        """Test sets folder to None when no target specified."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Test Folder"
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        # Should update subscriptions to None
        mock_session.query().filter_by().update.assert_called_with(
            {"folder": None}
        )

    def test_deletes_folder(self):
        """Test deletes the folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        mock_session.delete.assert_called_once_with(mock_folder)

    def test_commits_session(self):
        """Test commits session after delete."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        mock_session.commit.assert_called_once()

    def test_returns_true_on_success(self):
        """Test returns True on successful delete."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_session = Mock()
        mock_session.query().get.return_value = mock_folder

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

        assert result is True


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

    def test_returns_dict_with_folders_key(self):
        """Test returns dict with 'folders' key."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert "folders" in result
        assert isinstance(result["folders"], list)

    def test_returns_dict_with_uncategorized_key(self):
        """Test returns dict with 'uncategorized' key."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert "uncategorized" in result
        assert isinstance(result["uncategorized"], list)

    def test_includes_folder_data(self):
        """Test includes folder data for each folder."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_folder = Mock()
        mock_folder.name = "Test Folder"
        mock_folder.to_dict.return_value = {"id": "1", "name": "Test Folder"}

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

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

        assert len(result["folders"]) == 1
        assert result["folders"][0]["folder"]["name"] == "Test Folder"


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

    def test_returns_none_for_nonexistent_subscription(self):
        """Test returns None when subscription not found."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert result is None

    def test_updates_attributes(self):
        """Test updates subscription attributes."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.folder = "Old Folder"
        mock_session = Mock()
        mock_session.query().filter_by().first.return_value = mock_sub

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-123", folder="New Folder")

        assert mock_sub.folder == "New Folder"

    def test_does_not_update_id(self):
        """Test does not update subscription ID."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.id = "original-id"
        mock_session = Mock()
        mock_session.query().filter_by().first.return_value = mock_sub

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-123", id="new-id")

        assert mock_sub.id == "original-id"

    def test_recalculates_next_refresh(self):
        """Test recalculates next_refresh when interval changes."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.last_refresh = datetime(2024, 1, 1, 12, 0, tzinfo=timezone.utc)
        mock_sub.refresh_interval_minutes = 60
        mock_session = Mock()
        mock_session.query().filter_by().first.return_value = mock_sub

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

        # Should be 2 hours after last_refresh
        expected = mock_sub.last_refresh + timedelta(minutes=120)
        assert mock_sub.next_refresh == expected

    def test_uses_now_when_no_last_refresh(self):
        """Test uses current time when no last_refresh."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_sub = Mock()
        mock_sub.last_refresh = None
        mock_sub.refresh_interval_minutes = 60
        mock_session = Mock()
        mock_session.query().filter_by().first.return_value = mock_sub

        manager = FolderManager(mock_session)
        before = datetime.now(timezone.utc)
        manager.update_subscription("sub-123", refresh_interval_minutes=30)

        # next_refresh should be in the future
        assert mock_sub.next_refresh > before

    def test_updates_updated_at(self):
        """Test updates updated_at timestamp."""
        from local_deep_research.news.folder_manager import FolderManager

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

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-123", folder="Test")

        assert mock_sub.updated_at is not None

    def test_commits_session(self):
        """Test commits session after update."""
        from local_deep_research.news.folder_manager import FolderManager

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

        manager = FolderManager(mock_session)
        manager.update_subscription("sub-123", folder="Test")

        mock_session.commit.assert_called_once()

    def test_returns_updated_subscription(self):
        """Test returns the updated subscription."""
        from local_deep_research.news.folder_manager import FolderManager

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

        manager = FolderManager(mock_session)
        result = manager.update_subscription("sub-123", folder="Test")

        assert result is mock_sub


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

    def test_returns_false_for_nonexistent_subscription(self):
        """Test returns False when subscription not found."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert result is False

    def test_deletes_subscription(self):
        """Test deletes the subscription."""
        from local_deep_research.news.folder_manager import FolderManager

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

        manager = FolderManager(mock_session)
        manager.delete_subscription("sub-123")

        mock_session.delete.assert_called_once_with(mock_sub)

    def test_commits_session(self):
        """Test commits session after delete."""
        from local_deep_research.news.folder_manager import FolderManager

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

        manager = FolderManager(mock_session)
        manager.delete_subscription("sub-123")

        mock_session.commit.assert_called_once()

    def test_returns_true_on_success(self):
        """Test returns True on successful delete."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert result is True


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

    def test_returns_dict(self):
        """Test returns a dictionary."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert isinstance(result, dict)

    def test_includes_total_count(self):
        """Test includes total subscription count."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert result["total"] == 10

    def test_includes_active_count(self):
        """Test includes active subscription count."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        mock_session.query().count.return_value = 10
        mock_session.query().filter_by().count.return_value = 8
        mock_session.query().order_by().all.return_value = []

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

        assert "active" in result

    def test_includes_by_type_counts(self):
        """Test includes subscription counts by type."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        mock_session.query().count.return_value = 10
        mock_session.query().filter_by().count.return_value = 5
        mock_session.query().order_by().all.return_value = []

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

        assert "by_type" in result
        assert "search" in result["by_type"]
        assert "topic" in result["by_type"]

    def test_includes_folder_count(self):
        """Test includes folder count."""
        from local_deep_research.news.folder_manager import FolderManager

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

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

        assert result["folders"] == 2


class TestSubToDict:
    """Tests for _sub_to_dict helper method."""

    def test_includes_id(self):
        """Test includes subscription ID."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        mock_sub = Mock()
        mock_sub.id = "sub-123"
        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"

        result = manager._sub_to_dict(mock_sub)

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

    def test_includes_type(self):
        """Test includes subscription type."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        mock_sub = Mock()
        mock_sub.id = "sub-123"
        mock_sub.subscription_type = "topic"
        mock_sub.query_or_topic = "AI News"
        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"

        result = manager._sub_to_dict(mock_sub)

        assert result["type"] == "topic"

    def test_formats_datetime_as_iso(self):
        """Test formats datetime as ISO string."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        test_time = datetime(2024, 1, 15, 12, 30, 45, tzinfo=timezone.utc)
        mock_sub = Mock()
        mock_sub.id = "sub-123"
        mock_sub.subscription_type = "search"
        mock_sub.query_or_topic = "test"
        mock_sub.created_at = test_time
        mock_sub.last_refresh = test_time
        mock_sub.next_refresh = test_time
        mock_sub.refresh_interval_minutes = 60
        mock_sub.status = "active"

        result = manager._sub_to_dict(mock_sub)

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

    def test_handles_none_datetimes(self):
        """Test handles None datetime values."""
        from local_deep_research.news.folder_manager import FolderManager

        mock_session = Mock()
        manager = FolderManager(mock_session)

        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"

        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
