"""Timeout protection utilities for background tasks.

Provides helpers to wrap async task functions with asyncio.wait_for() timeout
protection. Ensures tasks complete within bounded time or fail gracefully with
proper logging and error handling.
"""

from __future__ import annotations

import asyncio
import time
from collections.abc import Awaitable
from typing import TypeVar

from app.utils.logging_compat import get_logger

log = get_logger(__name__)

T = TypeVar("T")


async def run_with_timeout(
    task_name: str,
    coro: Awaitable[T],
    timeout_seconds: int,
) -> T:
    """Run an async coroutine with timeout protection.

    Args:
        task_name: Human-readable name of the task for logging.
        coro: The coroutine to execute.
        timeout_seconds: Maximum seconds to wait before timeout.

    Raises:
        asyncio.TimeoutError: If the task exceeds the timeout.

    Returns:
        The return value of the coroutine.
    """
    start_time = time.monotonic()
    try:
        result: T = await asyncio.wait_for(coro, timeout=timeout_seconds)
        elapsed = time.monotonic() - start_time
        if elapsed > timeout_seconds * 0.8:
            log.warning(
                "task_approaching_timeout",
                task_name=task_name,
                timeout_seconds=timeout_seconds,
                elapsed_seconds=round(elapsed, 2),
                usage_percent=round((elapsed / timeout_seconds) * 100, 1),
            )
        return result
    except TimeoutError:
        elapsed = time.monotonic() - start_time
        log.warning(
            "task_timeout",
            task_name=task_name,
            timeout_seconds=timeout_seconds,
            elapsed_seconds=round(elapsed, 2),
        )
        raise

