"""Application runner that starts the FastAPI server with all services.

Entry point for running mm-base6 applications.
"""

import asyncio
import time
from collections.abc import Coroutine
from contextvars import Context
from typing import Any

from mm_telegram import PrivateBot, TelegramHandler

from mm_base6.core.core import CoreProtocol
from mm_base6.server.jinja import BaseJinjaConfig
from mm_base6.server.server import init_server
from mm_base6.server.uvicorn import serve_uvicorn


async def run[CoreType: CoreProtocol[Any, Any, Any, Any]](
    *,
    core: CoreType,
    jinja_config_cls: type[BaseJinjaConfig[CoreType]],
    telegram_handlers: list[TelegramHandler] | None = None,
    host: str,
    port: int,
    uvicorn_log_level: str,
) -> None:
    """Start the application server with all configured services.

    Initializes the event loop task factory, starts the Telegram bot if configured,
    and runs the FastAPI server via uvicorn.
    """
    loop = asyncio.get_running_loop()
    loop.set_task_factory(_custom_task_factory)

    # Core startup is already handled in Core.init, just call startup hook
    await core.startup()

    telegram_bot = None
    if telegram_handlers is not None:
        telegram_bot = PrivateBot(telegram_handlers, {"core": core})
        telegram_bot_settings = core.builtin_services.telegram.get_bot_settings()
        if telegram_bot_settings and telegram_bot_settings.auto_start:
            await telegram_bot.start(telegram_bot_settings.token, telegram_bot_settings.allowed_users)

    jinja_config = jinja_config_cls(core)
    fastapi_app = init_server(core, telegram_bot, jinja_config)
    # nosec: host/port are intentionally configurable by the application
    await serve_uvicorn(fastapi_app, host=host, port=port, log_level=uvicorn_log_level)


def _custom_task_factory(
    loop: asyncio.AbstractEventLoop,
    coro: Coroutine[Any, Any, Any],
    *,
    context: Context | None = None,
    name: str | None = None,
) -> asyncio.tasks.Task[Any]:
    """Create asyncio Task with injected start_time attribute for tracking task age."""
    task = asyncio.Task(coro, loop=loop, context=context, name=name)
    # Monkey-patch custom attribute onto Task for tracking task age
    task.start_time = time.time()  # type: ignore[attr-defined]
    return task
