"""Concurrency utilities for the Alloy Runtime SDK.

Provides bounded-parallelism helpers that work with any awaitables,
independent of pipeline context. Use these in scripts, CLI tools,
or anywhere you need to run multiple async operations concurrently
with a concurrency limit.
"""

import asyncio
from collections.abc import Awaitable
from typing import Any


async def parallel(
    coroutines: list[Awaitable[Any]],
    *,
    max_concurrency: int = 10,
) -> list[Any]:
    """Execute multiple awaitables concurrently with bounded parallelism.

    Uses an asyncio.Semaphore to cap the number of in-flight operations.
    Results are returned in the same order as the input coroutines.

    Args:
        coroutines: Awaitable operations to execute.
        max_concurrency: Maximum number of concurrent operations.
            Defaults to 10.

    Returns:
        List of results in the same order as input coroutines.

    Raises:
        Exception: Re-raises the first exception from any coroutine.

    Example:
        from alloy_runtime_sdk.concurrency import parallel

        results = await parallel(
            [fetch(url) for url in urls],
            max_concurrency=5,
        )
    """
    semaphore = asyncio.Semaphore(max_concurrency)

    async def run_with_semaphore(coro: Awaitable[Any]) -> Any:
        async with semaphore:
            return await coro

    results = await asyncio.gather(
        *[run_with_semaphore(coro) for coro in coroutines],
        return_exceptions=False,
    )

    return list(results)
