"""Standalone gateway server."""

from __future__ import annotations

import json
from dataclasses import dataclass
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Any

from memnixa.agent import AgentRuntime
from memnixa.channels.manager import ChannelManager
from memnixa.gateway.messages import InboundMessageContext
from memnixa.gateway.run_queue import InMemoryRunQueue
from memnixa.i18n import t


@dataclass(slots=True)
class GatewayApplication:
    """HTTP-facing gateway wrapper around the agent runtime."""

    runtime: AgentRuntime
    channel_manager: ChannelManager | None = None
    run_queue: InMemoryRunQueue | None = None

    def __post_init__(self) -> None:
        if self.run_queue is None and self.channel_manager is not None:
            self.run_queue = self.channel_manager.run_queue
        if self.run_queue is None:
            self.run_queue = InMemoryRunQueue()

    def health(self) -> dict[str, Any]:
        """Return gateway health."""
        default_provider = self.runtime.config.get_provider_config()
        return {
            "ok": True,
            "provider": default_provider.preset,
            "default_provider": default_provider.id,
            "model": default_provider.model,
            "workspace": str(self.runtime.config.workspace),
            "channels": self.channel_manager.get_status()
            if self.channel_manager
            else {},
            "queue": self.run_queue.snapshot() if self.run_queue else {},
        }

    def run_agent(self, payload: dict[str, Any]) -> dict[str, Any]:
        """Execute one agent turn from a gateway request."""
        body_for_agent = str(payload.get("body_for_agent") or "").strip()
        raw_body = str(payload.get("raw_body") or body_for_agent)
        if not body_for_agent:
            raise ValueError("body_for_agent is required")

        inbound = InboundMessageContext(
            session_id=str(payload.get("session_id") or "main"),
            run_id=str(payload["run_id"]) if payload.get("run_id") else None,
            message_id=str(payload["message_id"])
            if payload.get("message_id")
            else None,
            received_at=str(payload["received_at"])
            if payload.get("received_at")
            else None,
            raw_body=raw_body,
            body_for_agent=body_for_agent,
            source_mode=str(payload.get("source_mode") or "gateway_http"),
            channel=str(payload["channel"]) if payload.get("channel") else "http",
            launch_cwd=str(payload["launch_cwd"])
            if payload.get("launch_cwd")
            else None,
            entrypoint_path=str(payload["entrypoint_path"])
            if payload.get("entrypoint_path")
            else None,
            python_executable=str(payload["python_executable"])
            if payload.get("python_executable")
            else None,
            actor_identity_status=str(payload.get("actor_identity_status") or "unknown"),
            actor_user_id=str(payload["actor_user_id"])
            if payload.get("actor_user_id")
            else None,
            actor_role=str(payload.get("actor_role") or "unknown"),
            actor_external_id=str(payload["actor_external_id"])
            if payload.get("actor_external_id")
            else None,
            actor_is_owner=bool(payload.get("actor_is_owner", False)),
        )
        result = self.run_queue.enqueue(
            inbound.session_id,
            lambda: self.runtime.run_turn(inbound),
        ).result()
        return {
            "ok": True,
            "session_id": inbound.session_id,
            "run_id": inbound.run_id,
            "message_id": inbound.message_id,
            "reply": result.reply,
            "iterations": result.iterations,
            "tool_events": result.tool_events,
            "provider_id": result.provider_id,
            "provider_label": result.provider_label,
            "model_name": result.model_name,
        }

    def export_memory(self) -> dict[str, Any]:
        """Export all stored long-term memory items."""
        payload = self.runtime.session.export_memory_items()
        payload["ok"] = True
        return payload

    def shutdown(self) -> None:
        """Release queue workers owned by the gateway application."""
        if self.channel_manager:
            self.channel_manager.stop_background()
        if self.run_queue:
            self.run_queue.shutdown(wait=False)


def create_gateway_handler(
    application: GatewayApplication,
) -> type[BaseHTTPRequestHandler]:
    """Create a request handler bound to the given application."""

    class GatewayRequestHandler(BaseHTTPRequestHandler):
        def do_GET(self) -> None:  # noqa: N802
            if self.path == "/health":
                self._send_json(200, application.health())
                return
            if self.path == "/v1/memory/export":
                self._send_json(200, application.export_memory())
                return
            self._send_json(404, {"ok": False, "error": "not found"})

        def do_POST(self) -> None:  # noqa: N802
            if self.path != "/v1/agent/run":
                self._send_json(404, {"ok": False, "error": "not found"})
                return

            try:
                payload = self._read_json_body()
                response = application.run_agent(payload)
            except ValueError as exc:
                self._send_json(400, {"ok": False, "error": str(exc)})
                return
            except Exception as exc:  # pragma: no cover
                self._send_json(500, {"ok": False, "error": str(exc)})
                return

            self._send_json(200, response)

        def log_message(self, format: str, *args: Any) -> None:  # noqa: A003
            return

        def _read_json_body(self) -> dict[str, Any]:
            content_length = int(self.headers.get("Content-Length") or "0")
            if content_length <= 0:
                return {}
            raw = self.rfile.read(content_length)
            decoded = json.loads(raw.decode("utf-8"))
            if not isinstance(decoded, dict):
                raise ValueError("request body must be a JSON object")
            return decoded

        def _send_json(self, status_code: int, payload: dict[str, Any]) -> None:
            body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
            self.send_response(status_code)
            self.send_header("Content-Type", "application/json; charset=utf-8")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)

    return GatewayRequestHandler


def run_gateway_server(
    application: GatewayApplication,
    host: str,
    port: int,
) -> None:
    """Run the gateway forever."""
    if application.channel_manager:
        application.channel_manager.start_background()
    server = ThreadingHTTPServer((host, port), create_gateway_handler(application))
    print(t("gateway.listening", host=host, port=port))
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.server_close()
        application.shutdown()
