"""
Async runtime loader for releaseops bundles.

Wraps the synchronous RuntimeLoader with asyncio.to_thread() to provide
non-blocking bundle loading for async agent frameworks.
"""

from __future__ import annotations

import asyncio
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Any, AsyncGenerator, Dict, Optional, Tuple

from llmhq_releaseops.models.bundle import BundleManifest
from llmhq_releaseops.runtime.loader import RuntimeLoader
from llmhq_releaseops.telemetry.context import bundle_context
from llmhq_releaseops.telemetry.models import TelemetryContext

logger = logging.getLogger(__name__)


class AsyncRuntimeLoader:
    """
    Async wrapper around RuntimeLoader.

    All file I/O is offloaded to a thread via asyncio.to_thread()
    so the event loop is never blocked.

    Usage:
        loader = AsyncRuntimeLoader()
        bundle, metadata = await loader.load_bundle("agent@prod")
    """

    def __init__(
        self,
        base_dir: Path = None,
        auto_inject_telemetry: bool = True,
    ):
        self._sync_loader = RuntimeLoader(
            base_dir=base_dir,
            auto_inject_telemetry=auto_inject_telemetry,
        )

    async def load_bundle(
        self,
        bundle_ref: str,
        inject_telemetry: Optional[bool] = None,
    ) -> Tuple[BundleManifest, TelemetryContext]:
        """
        Load a bundle by reference, offloading I/O to a thread.

        Args:
            bundle_ref: Reference in 'bundle-id@environment' format.
            inject_telemetry: Override auto_inject_telemetry setting.

        Returns:
            Tuple of (BundleManifest, TelemetryContext).
        """
        return await asyncio.to_thread(
            self._sync_loader.load_bundle,
            bundle_ref,
            inject_telemetry,
        )

    async def load_bundle_content(self, bundle_ref: str) -> Dict[str, Any]:
        """
        Load bundle with resolved content, offloading I/O to a thread.

        Returns:
            Dict with keys: bundle, metadata, prompts, policies, model.
        """
        return await asyncio.to_thread(
            self._sync_loader.load_bundle_content,
            bundle_ref,
        )

    @asynccontextmanager
    async def load_bundle_context(
        self, bundle_ref: str
    ) -> AsyncGenerator[BundleManifest, None]:
        """
        Async context manager that loads bundle with telemetry, cleans up on exit.

        Usage:
            async with loader.load_bundle_context("agent@prod") as bundle:
                await agent.run(bundle)
        """
        bundle, metadata = await self.load_bundle(
            bundle_ref, inject_telemetry=False,
        )
        with bundle_context(metadata):
            try:
                otel = self._sync_loader._get_otel_integration()
                otel.inject_current_span(metadata)
            except Exception as e:
                logger.warning(f"Telemetry injection failed (non-fatal): {e}")
            yield bundle
