"""Bounded asynchronous relevance scoring, not a speed benchmark or model call.

A fixed worker pool bounds both task count and in-flight scorer operations.
The scorer seam can represent a remote model, but this example is offline.
"""
import asyncio
import math
from dataclasses import dataclass
from memory_index import Memory, sample_memories
from routing import terms


@dataclass(frozen=True)
class Result:
    memory: Memory
    score: float | None
    error: str | None = None


async def lexical_scorer(query, memory):
    await asyncio.sleep(0)  # Scheduling point; no simulated latency/speed claim.
    wanted = terms(query)
    return len(wanted & terms(memory.body)) / max(1, len(wanted))


async def rank(query, memories, concurrency=4, scorer=lexical_scorer, timeout=2.0):
    if not isinstance(concurrency, int) or not 1 <= concurrency <= 500:
        raise ValueError("concurrency must be an integer from 1 to 500")
    if not math.isfinite(timeout) or timeout <= 0:
        raise ValueError("timeout must be finite and positive")
    iterator = iter(memories)
    results = []

    async def worker():
        # next() occurs without an await: workers cannot consume the same item.
        for memory in iterator:
            try:
                score = float(await asyncio.wait_for(scorer(query, memory), timeout))
                if not math.isfinite(score) or not 0 <= score <= 1:
                    raise ValueError("scorer returned an invalid relevance score")
                results.append(Result(memory, score))
            except Exception as exc:
                # Failure means unknown, not zero relevance or counter-evidence.
                results.append(Result(memory, None, type(exc).__name__))

    tasks = [asyncio.create_task(worker()) for _ in range(concurrency)]
    try:
        await asyncio.gather(*tasks)
    finally:
        for task in tasks:
            task.cancel()
        await asyncio.gather(*tasks, return_exceptions=True)
    return sorted(results, key=lambda r: (r.score is None,
                                         -(r.score or 0), r.memory.id))


if __name__ == "__main__":
    for result in asyncio.run(rank("atlas launch security", sample_memories())):
        print(result.memory.id, result.score, result.error)
