# utils/task_tracker.py
"""
Tracked asyncio task creation utility.

Replaces fire-and-forget asyncio.create_task() calls with error-logging wrappers
so that exceptions in background tasks are never silently swallowed.
"""

import asyncio
from typing import Optional, Coroutine, Set
from utils.logger import get_logger

logger = get_logger("task_tracker")

# Keep references to running tasks so they aren't garbage-collected
_active_tasks: Set[asyncio.Task] = set()


def create_tracked_task(
    coro: Coroutine,
    task_name: Optional[str] = None,
) -> asyncio.Task:
    """
    Create an asyncio task with automatic error logging.

    Unlike bare asyncio.create_task(), this wrapper:
    - Names the task for easier debugging
    - Logs any unhandled exception (with full traceback) instead of swallowing it
    - Keeps a strong reference so the task isn't garbage-collected mid-flight
    - Auto-cleans the reference set when the task finishes

    Args:
        coro: The coroutine to schedule.
        task_name: Human-readable name for log messages.
                   Defaults to the coroutine's __qualname__.

    Returns:
        The created asyncio.Task (tracked internally).
    """
    name = task_name or getattr(coro, "__qualname__", str(coro))

    async def _wrapped():
        try:
            await coro
        except asyncio.CancelledError:
            logger.debug(f"Task '{name}' was cancelled")
            raise  # re-raise so Task.cancelled() works correctly
        except Exception:
            logger.error(f"Background task '{name}' failed", exc_info=True)

    task = asyncio.create_task(_wrapped(), name=name)
    _active_tasks.add(task)
    task.add_done_callback(_active_tasks.discard)
    return task
