"""Task-oriented capability index for the public infra_core surface.

Use this module when you need to discover which stable helper fits a workflow.
The index is intentionally small and points to public imports rather than
internal implementation details.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Final


@dataclass(frozen=True, slots=True)
class Capability:
    """Describe one task-oriented public capability exposed by infra_core."""

    slug: str
    task: str
    preferred_import: str
    summary: str
    keywords: tuple[str, ...] = ()
    extras: tuple[str, ...] = ()
    example: str = ""

    def searchable_text(self) -> str:
        """Return the lowercase text used for simple free-text matching."""
        return " ".join(
            (
                self.slug,
                self.task,
                self.preferred_import,
                self.summary,
                " ".join(self.keywords),
                " ".join(self.extras),
                self.example,
            )
        ).lower()


CAPABILITIES: Final[tuple[Capability, ...]] = (
    Capability(
        slug="http-fetch",
        task="Retried HTTP requests",
        preferred_import="from infra_core import fetch, fetch_async, request_async",
        summary="Use for HTTP calls that need retry/backoff defaults and sanitized URL logging.",
        keywords=("http", "https", "request", "fetch", "retry", "backoff", "json"),
        example="response = await fetch_async('https://example.com/status')",
    ),
    Capability(
        slug="status-polling",
        task="Poll remote job status",
        preferred_import="from infra_core import poll_until, poll_until_async, StatusPoller",
        summary="Use for polling status endpoints or long-running jobs until they reach terminal states.",
        keywords=("poll", "status", "wait", "job", "operation", "backoff", "timeout", "404"),
        example="result = await poll_until_async(fetch_fn=fetch, is_terminal=is_done)",
    ),
    Capability(
        slug="task-runtime",
        task="Run bounded async work",
        preferred_import="from infra_core import TaskRuntime, RuntimeConfig",
        summary="Use for bounded asyncio concurrency, cooperative cancellation, and per-task timeout handling.",
        keywords=("asyncio", "concurrency", "semaphore", "timeout", "cancel", "task", "worker"),
        example="runtime = TaskRuntime(config=RuntimeConfig(concurrency=8))",
    ),
    Capability(
        slug="asset-download",
        task="Download files with retries",
        preferred_import="from infra_core import download_asset, download_asset_async, AssetDownloadClient",
        summary="Use for downloading files with retry logic, connection reuse, and optional checksums.",
        keywords=("download", "file", "asset", "checksum", "retry", "httpx"),
        example="result = download_asset(url, Path('assets/file.bin'))",
    ),
    Capability(
        slug="azure-blob-storage",
        task="Mirror files to Azure Blob",
        preferred_import="from infra_core.azure import AzureStorageSettings, get_client",
        summary="Use for explicit Azure Blob configuration, sync/async uploads, downloads, and local mirroring.",
        keywords=("azure", "blob", "storage", "upload", "download", "mirror", "container"),
        extras=("azure",),
        example="settings, mode = AzureStorageSettings.from_env(); client = get_client(settings, mode)",
    ),
    Capability(
        slug="run-manifest",
        task="Build run manifests",
        preferred_import="from infra_core.azure import build_manifest",
        summary="Use for artifact manifests with file metadata, inferred types, and blob URLs.",
        keywords=("manifest", "artifact", "run", "output", "blob", "storage"),
        extras=("azure",),
        example="manifest = build_manifest(storage_path, client=azure_client)",
    ),
    Capability(
        slug="azure-function-http",
        task="Call Azure Functions",
        preferred_import="from infra_core.azure import AzureFunctionAsyncClient, AzureFunctionSyncClient",
        summary="Use for Azure Function HTTP calls with API-key injection and retry/timeout overrides.",
        keywords=("azure", "function", "http", "api", "function app", "sync", "async"),
        extras=("azure",),
        example="client = AzureFunctionAsyncClient(base_url, api_key=api_key)",
    ),
    Capability(
        slug="azure-table-job-store",
        task="Track jobs in Azure Table",
        preferred_import="from infra_core.azure import AzureTableJobStore, TableConfig",
        summary=(
            "Use for storing per-job records, using connection strings or "
            "DefaultAzureCredential, and listing runs by batch ID in Azure Table Storage."
        ),
        keywords=("azure", "table", "job", "store", "run", "batch", "metadata"),
        extras=("azure",),
        example="with AzureTableJobStore(record_type=Record, ...) as store: ...",
    ),
    Capability(
        slug="azure-monitoring",
        task="Emit heartbeats and run logs",
        preferred_import="from infra_core.azure import HeartbeatMonitor, RunLogWriter, TelemetryClient",
        summary="Use for heartbeat files, mirrored run logs, and lightweight telemetry events.",
        keywords=("azure", "heartbeat", "telemetry", "run log", "monitoring", "health"),
        extras=("azure",),
        example="monitor = HeartbeatMonitor(storage_root, azure_client=client)",
    ),
)


def find_capabilities(query: str) -> list[Capability]:
    """Return capabilities matching a free-text query.

    Matches are ranked by the number of query terms found in each capability's
    searchable text. An empty query returns the full index.
    """

    normalized = query.strip().lower()
    if not normalized:
        return list(CAPABILITIES)

    terms = tuple(dict.fromkeys(normalized.split()))
    ranked: list[tuple[int, Capability]] = []
    for capability in CAPABILITIES:
        haystack = capability.searchable_text()
        score = sum(1 for term in terms if term in haystack)
        if normalized in haystack:
            score += len(terms)
        if score:
            ranked.append((score, capability))

    ranked.sort(key=lambda item: (-item[0], item[1].task))
    return [capability for _, capability in ranked]


__all__ = ["CAPABILITIES", "Capability", "find_capabilities"]
