"""ChatMixin — handlers for text-generation endpoints.

  POST /v1/chat/completions
  POST /v1/completions
  POST /v1/embeddings
"""
from __future__ import annotations


class ChatMixin:
    """Provides text-generation endpoint handlers.

    Relies on the concrete class for:
      self._resolve_ctx()  self._send_json()  self._not_supported()
      self._read_body()    self._start_sse()  self._sse_write()
      self.wfile           (from BaseHTTPRequestHandler)
    """

    # ── chat completions ──────────────────────────────────────────────────────

    def _handle_chat_completions(self) -> None:
        body = self._read_body()
        if body is None:
            return

        ctx = self._resolve_ctx(body.get("model"))
        if ctx.infer is None:
            self._not_supported("/v1/chat/completions")
            return

        messages = body.get("messages")
        if not messages or not isinstance(messages, list):
            self._send_json(400, {"error": "'messages' must be a non-empty list"})
            return

        max_tokens  = int(body.get("max_tokens", ctx.default_max_tokens))
        temperature = float(body.get("temperature", 0.7))

        if body.get("stream") and ctx.infer_stream is not None:
            self._start_sse()
            try:
                for token in ctx.infer_stream(messages, max_tokens, temperature):
                    self._sse_write({
                        "object": "chat.completion.chunk",
                        "model": ctx.model_id,
                        "choices": [{
                            "index": 0,
                            "delta": {"content": token},
                            "finish_reason": None,
                        }],
                    })
                self.wfile.write(b"data: [DONE]\n\n")
                self.wfile.flush()
            except Exception as exc:  # pylint: disable=broad-except
                self._sse_write({"error": str(exc)})
            return

        try:
            text = ctx.infer(messages, max_tokens, temperature)
        except Exception as exc:  # pylint: disable=broad-except
            self._send_json(500, {"error": str(exc)})
            return

        self._send_json(200, {
            "object": "chat.completion",
            "model": ctx.model_id,
            "choices": [{
                "index": 0,
                "message": {"role": "assistant", "content": text},
                "finish_reason": "stop",
            }],
            "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
        })

    # ── legacy completions ────────────────────────────────────────────────────

    def _handle_completions(self) -> None:
        body = self._read_body()
        if body is None:
            return

        ctx = self._resolve_ctx(body.get("model"))
        if ctx.infer is None:
            self._not_supported("/v1/completions")
            return

        prompt = body.get("prompt", "")
        if not prompt:
            self._send_json(400, {"error": "'prompt' is required"})
            return

        max_tokens  = int(body.get("max_tokens", ctx.default_max_tokens))
        temperature = float(body.get("temperature", 0.7))
        messages    = [{"role": "user", "content": prompt}]

        if body.get("stream") and ctx.infer_stream is not None:
            self._start_sse()
            try:
                for token in ctx.infer_stream(messages, max_tokens, temperature):
                    self._sse_write({
                        "object": "text_completion",
                        "model": ctx.model_id,
                        "choices": [{"index": 0, "text": token, "finish_reason": None}],
                    })
                self.wfile.write(b"data: [DONE]\n\n")
                self.wfile.flush()
            except Exception as exc:  # pylint: disable=broad-except
                self._sse_write({"error": str(exc)})
            return

        try:
            text = ctx.infer(messages, max_tokens, temperature)
        except Exception as exc:  # pylint: disable=broad-except
            self._send_json(500, {"error": str(exc)})
            return

        self._send_json(200, {
            "object": "text_completion",
            "model": ctx.model_id,
            "choices": [{"index": 0, "text": text, "finish_reason": "stop"}],
            "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
        })

    # ── embeddings ────────────────────────────────────────────────────────────

    def _handle_embeddings(self) -> None:
        body = self._read_body()
        if body is None:
            return

        ctx = self._resolve_ctx(body.get("model"))
        if ctx.embed is None:
            self._not_supported("/v1/embeddings")
            return

        inp = body.get("input")
        if inp is None:
            self._send_json(400, {"error": "'input' is required"})
            return

        texts = [inp] if isinstance(inp, str) else list(inp)

        try:
            vectors = ctx.embed(texts)
        except Exception as exc:  # pylint: disable=broad-except
            self._send_json(500, {"error": str(exc)})
            return

        self._send_json(200, {
            "object": "list",
            "model": ctx.model_id,
            "data": [
                {"index": i, "object": "embedding", "embedding": vec}
                for i, vec in enumerate(vectors)
            ],
            "usage": {"prompt_tokens": 0, "total_tokens": 0},
        })
