"""llmpm.core.server — OpenAI-compatible HTTP server for loaded models.

Public API::

    from llmpm.core import server

    # single model
    server.start(model_id="owner/my-model", infer=my_infer_fn)

    # multiple models — single server, routes via `model` field in request body
    server.start_multi([ctx1, ctx2, ...], host="localhost", port=8080)

Unsupported endpoints return HTTP 501 automatically.
"""
from __future__ import annotations

from http.server import ThreadingHTTPServer

from ._context import HandlerContext
from ._handler import _Handler
from ._types import (
    EmbedFn,
    InferFn,
    InferImageEditFn,
    InferImageFn,
    InferStreamFn,
    SynthesizeFn,
    TranscribeFn,
)

__all__ = [
    "start",
    "start_multi",
    "HandlerContext",
    "InferFn",
    "InferStreamFn",
    "EmbedFn",
    "InferImageFn",
    "InferImageEditFn",
    "TranscribeFn",
    "SynthesizeFn",
]


def _make_handler(contexts: dict[str, HandlerContext]) -> type:
    """Return a request-handler class bound to a dict of contexts."""

    class _BoundHandler(_Handler):
        _contexts = contexts

    _BoundHandler.__name__ = "_BoundHandler"
    return _BoundHandler


def start_multi(
    contexts: list[HandlerContext],
    *,
    host: str = "0.0.0.0",
    port: int = 8080,
) -> None:
    """Start a single blocking server for one or more models.

    Requests are routed to the correct model via the ``model`` field in the
    request body.  If absent, the first loaded model is used as the default.
    Blocks until Ctrl-C.
    """
    ctx_map = {ctx.model_id: ctx for ctx in contexts}
    handler = _make_handler(ctx_map)
    httpd   = ThreadingHTTPServer((host, port), handler)
    httpd.serve_forever()


def start(
    model_id: str,
    infer: InferFn | None = None,
    *,
    host: str = "0.0.0.0",
    port: int = 8080,
    default_max_tokens: int = 128_000,
    infer_stream: InferStreamFn | None = None,
    embed: EmbedFn | None = None,
    infer_image: InferImageFn | None = None,
    infer_image_edit: InferImageEditFn | None = None,
    transcribe: TranscribeFn | None = None,
    synthesize: SynthesizeFn | None = None,
    category: str = "text-generation",
) -> None:
    """Start a blocking server for a single model (convenience wrapper)."""
    ctx = HandlerContext(
        model_id=model_id,
        default_max_tokens=default_max_tokens,
        category=category,
        infer=infer,
        infer_stream=infer_stream,
        embed=embed,
        infer_image=infer_image,
        infer_image_edit=infer_image_edit,
        transcribe=transcribe,
        synthesize=synthesize,
    )
    start_multi([ctx], host=host, port=port)
