"""
Automatic cleanup of idle database connections.

Periodically closes database connections for users who have no active sessions
and no active research, preventing resource leaks when users close their browser
without logging out.
"""

from apscheduler.schedulers.background import BackgroundScheduler
from loguru import logger

from ...database.session_passwords import session_password_store
from ...database.thread_local_session import cleanup_dead_threads
from ...web.routes.globals import get_usernames_with_active_research


def cleanup_idle_connections(session_manager, db_manager):
    """Close db connections for users with no active sessions and no active research."""
    # 1. Purge expired sessions first
    session_manager.cleanup_expired_sessions()

    # 2. Get protected usernames (active sessions OR active research)
    active_usernames = session_manager.get_active_usernames()
    researching_usernames = get_usernames_with_active_research()
    protected = active_usernames | researching_usernames

    # 3. Get usernames with open connections
    connected_usernames = db_manager.get_connected_usernames()

    # 4. Find idle candidates
    candidates = connected_usernames - protected

    # 5. Double-check before closing (narrows race window)
    closed = 0
    for username in candidates:
        if session_manager.has_active_sessions_for(username):
            logger.debug(
                f"Skipped {username} (active session appeared since snapshot)"
            )
            continue  # User logged in since snapshot
        if username in get_usernames_with_active_research():
            logger.debug(
                f"Skipped {username} (active research appeared since snapshot)"
            )
            continue  # Research started since snapshot
        # Unregister news scheduler jobs (matches logout pattern in routes.py)
        try:
            from ...news.subscription_manager.scheduler import (
                get_news_scheduler,
            )

            sched = get_news_scheduler()
            if sched.is_running:
                sched.unregister_user(username)
        except Exception:
            logger.warning(
                f"Failed to unregister scheduler for {username}",
            )
        try:
            db_manager.close_user_database(username)
            session_password_store.clear_all_for_user(username)
            closed += 1
            logger.debug(f"Closed idle connection for {username}")
        except Exception:
            logger.warning(f"Connection cleanup failed for {username}")

    if closed:
        logger.info(f"Connection cleanup: closed {closed} idle connection(s)")
    logger.debug(
        f"Connection cleanup: evaluated {len(candidates)} candidate(s), "
        f"closed {closed}, protected {len(protected)} active user(s)"
    )

    # Sweep dead-thread engines and sessions — safety net when neither HTTP
    # requests nor the queue processor are triggering sweeps.
    # Engine sweep is rate-limited internally (at most once per 60s);
    # session sweep runs unconditionally but is lightweight.
    cleanup_dead_threads()


def start_connection_cleanup_scheduler(
    session_manager, db_manager, interval_seconds=300
):
    """Start APScheduler job for periodic connection cleanup.

    Args:
        session_manager: The SessionManager singleton.
        db_manager: The DatabaseManager singleton.
        interval_seconds: How often to run cleanup (default: 5 minutes).

    Returns:
        The BackgroundScheduler instance (for shutdown registration).
    """
    scheduler = BackgroundScheduler()
    scheduler.add_job(
        cleanup_idle_connections,
        "interval",
        seconds=interval_seconds,
        args=[session_manager, db_manager],
        id="cleanup_idle_connections",
        jitter=30,
    )
    scheduler.start()
    logger.info(
        f"Connection cleanup scheduler started "
        f"(interval={interval_seconds}s, jitter=30s)"
    )
    return scheduler
