"""Mock OpenAI provider server for integration testing.

A lightweight HTTP server that mimics the OpenAI Chat Completions API.
Runs on a background thread in the same Python process, allowing
per-test response configuration via direct method calls.
"""

from __future__ import annotations

import json
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Any


class MockOpenAIHandler(BaseHTTPRequestHandler):
    """HTTP handler that returns configured responses."""

    server: MockOpenAIServer  # type: ignore[assignment]

    def do_POST(self) -> None:
        if self.path == "/v1/chat/completions":
            self._handle_completions()
        elif self.path == "/v1/messages":
            self._handle_completions()  # Same handler for now
        else:
            self.send_error(404, f"Unknown path: {self.path}")

    def _handle_completions(self) -> None:
        # Read request body (for potential future use)
        content_length = int(self.headers.get("Content-Length", 0))
        if content_length > 0:
            self.rfile.read(content_length)

        provider = self.server.provider

        # Snapshot state under lock to avoid torn reads
        with provider._lock:
            error_response = provider._error_response
            streaming_response = provider._streaming_response
            response = provider._response or _default_response()
            response_status = provider._response_status or 200

        if error_response:
            err_status, err_body = error_response
            self.send_response(err_status)
            self.send_header("Content-Type", "application/json")
            self.end_headers()
            self.wfile.write(json.dumps(err_body).encode())
            return

        if streaming_response:
            chunks, usage = streaming_response
            self.send_response(200)
            self.send_header("Content-Type", "text/event-stream")
            self.send_header("Cache-Control", "no-cache")
            self.end_headers()

            for _i, content in enumerate(chunks):
                chunk = {
                    "id": "chatcmpl-mock",
                    "object": "chat.completion.chunk",
                    "model": "gpt-4o",
                    "choices": [{"index": 0, "delta": {"content": content}, "finish_reason": None}],
                }
                self.wfile.write(f"data: {json.dumps(chunk)}\n\n".encode())
                self.wfile.flush()

            # Final chunk with finish_reason + usage
            final = {
                "id": "chatcmpl-mock",
                "object": "chat.completion.chunk",
                "model": "gpt-4o",
                "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}],
            }
            if usage:
                final["usage"] = usage
            self.wfile.write(f"data: {json.dumps(final)}\n\n".encode())
            self.wfile.write(b"data: [DONE]\n\n")
            self.wfile.flush()
            return

        # Default non-streaming response
        self.send_response(response_status)
        self.send_header("Content-Type", "application/json")
        self.end_headers()
        self.wfile.write(json.dumps(response).encode())

    def log_message(self, format: str, *args: Any) -> None:
        """Suppress log output during tests."""
        pass


class MockOpenAIServer(HTTPServer):
    """HTTPServer subclass that holds a reference to the provider."""

    provider: MockOpenAIProvider


class MockOpenAIProvider:
    """Mock OpenAI API server for integration testing.

    Runs on a background thread. Configure responses per-test
    via set_response(), set_streaming_response(), or set_error().
    Call reset() between tests.

    Usage:
        provider = MockOpenAIProvider(port=18081)
        provider.start()

        provider.set_response({"content": "Hello!"})
        # ... SDK makes request through Rungate ...

        provider.reset()
        provider.stop()
    """

    def __init__(self, host: str = "127.0.0.1", port: int = 18081) -> None:
        self.host = host
        self.port = port
        self._server: MockOpenAIServer | None = None
        self._thread: threading.Thread | None = None
        self._lock = threading.Lock()
        self._response: dict[str, Any] | None = None
        self._response_status: int | None = None
        self._streaming_response: tuple[list[str], dict[str, Any] | None] | None = None
        self._error_response: tuple[int, dict[str, Any]] | None = None

    def start(self) -> None:
        """Start the mock server on a background thread."""
        self._server = MockOpenAIServer((self.host, self.port), MockOpenAIHandler)
        self._server.provider = self
        self._thread = threading.Thread(target=self._server.serve_forever, daemon=True)
        self._thread.start()

    def stop(self) -> None:
        """Stop the mock server."""
        if self._server:
            self._server.shutdown()
            self._server.server_close()
        if self._thread:
            self._thread.join(timeout=5)

    def reset(self) -> None:
        """Clear all configured responses."""
        with self._lock:
            self._response = None
            self._response_status = None
            self._streaming_response = None
            self._error_response = None

    def set_response(
        self,
        content: str = "Hello from mock!",
        model: str = "gpt-4o",
        tokens: dict[str, int] | None = None,
    ) -> None:
        """Set a non-streaming chat completion response."""
        usage = tokens or {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15}
        with self._lock:
            self._response = {
                "id": "chatcmpl-mock-123",
                "object": "chat.completion",
                "model": model,
                "created": 1700000000,
                "choices": [
                    {
                        "index": 0,
                        "message": {"role": "assistant", "content": content},
                        "finish_reason": "stop",
                    }
                ],
                "usage": usage,
            }
            self._response_status = 200

    def set_streaming_response(
        self,
        chunks: list[str],
        tokens: dict[str, int] | None = None,
    ) -> None:
        """Set a streaming SSE response with the given content chunks."""
        usage = tokens or {"prompt_tokens": 10, "completion_tokens": len(chunks), "total_tokens": 10 + len(chunks)}
        with self._lock:
            self._streaming_response = (chunks, usage)

    def set_error(self, status: int = 500, message: str = "Internal Server Error") -> None:
        """Set an error response."""
        with self._lock:
            self._error_response = (status, {"error": {"message": message, "type": "server_error"}})


def _default_response() -> dict[str, Any]:
    return {
        "id": "chatcmpl-mock-default",
        "object": "chat.completion",
        "model": "gpt-4o",
        "created": 1700000000,
        "choices": [
            {
                "index": 0,
                "message": {"role": "assistant", "content": "Default mock response"},
                "finish_reason": "stop",
            }
        ],
        "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
    }
