"""
Tests for web/auth/session_manager.py

Tests cover:
- SessionManager class
- Session creation, validation, and destruction
- Session cleanup and expiration
- User session management
"""

import datetime
from datetime import UTC
from unittest.mock import patch


class TestSessionManagerInit:
    """Tests for SessionManager initialization."""

    def test_init_creates_empty_sessions_dict(self):
        """Should initialize with empty sessions dict."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        assert manager.sessions == {}

    def test_init_sets_session_timeout(self):
        """Should set default session timeout to 2 hours."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        assert manager.session_timeout == datetime.timedelta(hours=2)

    def test_init_sets_remember_me_timeout(self):
        """Should set remember_me timeout to 30 days."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        assert manager.remember_me_timeout == datetime.timedelta(days=30)


class TestCreateSession:
    """Tests for create_session method."""

    def test_create_session_returns_session_id(self):
        """Should return a session ID string."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")
        assert isinstance(session_id, str)
        assert (
            len(session_id) > 20
        )  # token_urlsafe(32) generates ~43 char string

    def test_create_session_stores_in_sessions_dict(self):
        """Should store session data in sessions dict."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")
        assert session_id in manager.sessions
        assert manager.sessions[session_id]["username"] == "testuser"

    def test_create_session_stores_username(self):
        """Should store the correct username."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("myuser123")
        assert manager.sessions[session_id]["username"] == "myuser123"

    def test_create_session_stores_created_at_timestamp(self):
        """Should store created_at timestamp."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        before = datetime.datetime.now(UTC)
        session_id = manager.create_session("testuser")
        after = datetime.datetime.now(UTC)

        created_at = manager.sessions[session_id]["created_at"]
        assert before <= created_at <= after

    def test_create_session_stores_last_access_timestamp(self):
        """Should store last_access timestamp."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        before = datetime.datetime.now(UTC)
        session_id = manager.create_session("testuser")
        after = datetime.datetime.now(UTC)

        last_access = manager.sessions[session_id]["last_access"]
        assert before <= last_access <= after

    def test_create_session_default_remember_me_false(self):
        """Should default remember_me to False."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")
        assert manager.sessions[session_id]["remember_me"] is False

    def test_create_session_with_remember_me_true(self):
        """Should set remember_me to True when specified."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser", remember_me=True)
        assert manager.sessions[session_id]["remember_me"] is True

    def test_create_session_generates_unique_ids(self):
        """Should generate unique session IDs."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_ids = [manager.create_session(f"user{i}") for i in range(100)]
        assert len(set(session_ids)) == 100  # All unique


class TestValidateSession:
    """Tests for validate_session method."""

    def test_validate_session_returns_username_for_valid_session(self):
        """Should return username for valid session."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")
        result = manager.validate_session(session_id)
        assert result == "testuser"

    def test_validate_session_returns_none_for_invalid_session(self):
        """Should return None for invalid session ID."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        result = manager.validate_session("nonexistent_session_id")
        assert result is None

    def test_validate_session_returns_none_for_expired_session(self):
        """Should return None for expired session."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        # Set last_access to past expired time
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(hours=3)

        result = manager.validate_session(session_id)
        assert result is None

    def test_validate_session_destroys_expired_session(self):
        """Should destroy expired sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        # Set last_access to expired
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(hours=3)

        manager.validate_session(session_id)
        assert session_id not in manager.sessions

    def test_validate_session_updates_last_access(self):
        """Should update last_access for valid session."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        # Set last_access to past
        old_time = datetime.datetime.now(UTC) - datetime.timedelta(minutes=30)
        manager.sessions[session_id]["last_access"] = old_time

        manager.validate_session(session_id)

        new_time = manager.sessions[session_id]["last_access"]
        assert new_time > old_time

    def test_validate_session_uses_remember_me_timeout(self):
        """Should use remember_me timeout for remembered sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser", remember_me=True)

        # Set last_access to 3 hours ago (would expire regular session)
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(hours=3)

        result = manager.validate_session(session_id)
        assert result == "testuser"  # Should still be valid

    def test_validate_session_expires_old_remember_me_session(self):
        """Should expire remember_me session after 30 days."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser", remember_me=True)

        # Set last_access to 31 days ago
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(days=31)

        result = manager.validate_session(session_id)
        assert result is None


class TestDestroySession:
    """Tests for destroy_session method."""

    def test_destroy_session_removes_from_sessions(self):
        """Should remove session from sessions dict."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")
        assert session_id in manager.sessions

        manager.destroy_session(session_id)
        assert session_id not in manager.sessions

    def test_destroy_session_handles_nonexistent_session(self):
        """Should handle destroying nonexistent session gracefully."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        # Should not raise exception
        manager.destroy_session("nonexistent_session")

    @patch("gc.collect")
    def test_destroy_session_triggers_gc(self, mock_gc):
        """Should trigger garbage collection."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        manager.destroy_session(session_id)
        mock_gc.assert_called_once()


class TestCleanupExpiredSessions:
    """Tests for cleanup_expired_sessions method."""

    def test_cleanup_removes_expired_regular_sessions(self):
        """Should remove expired regular sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        # Set to expired
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(hours=3)

        manager.cleanup_expired_sessions()
        assert session_id not in manager.sessions

    def test_cleanup_keeps_valid_sessions(self):
        """Should keep valid sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser")

        manager.cleanup_expired_sessions()
        assert session_id in manager.sessions

    def test_cleanup_removes_multiple_expired_sessions(self):
        """Should remove all expired sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()

        # Create multiple sessions
        expired_ids = []
        for i in range(5):
            sid = manager.create_session(f"user{i}")
            manager.sessions[sid]["last_access"] = datetime.datetime.now(
                UTC
            ) - datetime.timedelta(hours=3)
            expired_ids.append(sid)

        valid_id = manager.create_session("validuser")

        manager.cleanup_expired_sessions()

        for expired_id in expired_ids:
            assert expired_id not in manager.sessions
        assert valid_id in manager.sessions

    def test_cleanup_respects_remember_me_timeout(self):
        """Should respect remember_me timeout during cleanup."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        session_id = manager.create_session("testuser", remember_me=True)

        # Set to 3 hours ago (expired for regular, not for remember_me)
        manager.sessions[session_id]["last_access"] = datetime.datetime.now(
            UTC
        ) - datetime.timedelta(hours=3)

        manager.cleanup_expired_sessions()
        assert session_id in manager.sessions

    def test_cleanup_handles_empty_sessions(self):
        """Should handle empty sessions dict."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        # Should not raise exception
        manager.cleanup_expired_sessions()


class TestGetActiveSessionsCount:
    """Tests for get_active_sessions_count method."""

    def test_returns_zero_for_empty_sessions(self):
        """Should return 0 for empty sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        assert manager.get_active_sessions_count() == 0

    def test_returns_correct_count(self):
        """Should return correct count of sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        for i in range(5):
            manager.create_session(f"user{i}")

        assert manager.get_active_sessions_count() == 5

    def test_excludes_expired_sessions(self):
        """Should exclude expired sessions from count."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()

        # Create 5 sessions, expire 2
        for i in range(5):
            sid = manager.create_session(f"user{i}")
            if i < 2:
                manager.sessions[sid]["last_access"] = datetime.datetime.now(
                    UTC
                ) - datetime.timedelta(hours=3)

        assert manager.get_active_sessions_count() == 3


class TestGetUserSessions:
    """Tests for get_user_sessions method."""

    def test_returns_empty_list_for_no_sessions(self):
        """Should return empty list if user has no sessions."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        result = manager.get_user_sessions("testuser")
        assert result == []

    def test_returns_user_sessions(self):
        """Should return sessions for the specified user."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        manager.create_session("testuser")
        manager.create_session("testuser")
        manager.create_session("otheruser")

        result = manager.get_user_sessions("testuser")
        assert len(result) == 2

    def test_session_id_is_masked(self):
        """Should mask session ID to first 8 chars."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        manager.create_session("testuser")

        result = manager.get_user_sessions("testuser")
        assert result[0]["session_id"].endswith("...")
        assert len(result[0]["session_id"]) == 11  # 8 chars + "..."

    def test_returns_session_info(self):
        """Should return correct session information."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        manager.create_session("testuser", remember_me=True)

        result = manager.get_user_sessions("testuser")
        assert "session_id" in result[0]
        assert "created_at" in result[0]
        assert "last_access" in result[0]
        assert "remember_me" in result[0]
        assert result[0]["remember_me"] is True

    def test_does_not_return_other_users_sessions(self):
        """Should not return sessions from other users."""
        from local_deep_research.web.auth.session_manager import SessionManager

        manager = SessionManager()
        manager.create_session("user1")
        manager.create_session("user2")
        manager.create_session("user3")

        result = manager.get_user_sessions("user1")
        assert len(result) == 1
