"""Threaded standard-library HTTP adapter for the portal application."""

from __future__ import annotations

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from typing import Type

from arr_web.app import PortalApplication
from arr_web.contracts import MAX_UPLOAD_BYTES, PortalError, Response, failure


SECURITY_HEADERS = {
    "Content-Security-Policy": (
        "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; "
        "connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; "
        "form-action 'self'"
    ),
    "Referrer-Policy": "no-referrer",
    "X-Content-Type-Options": "nosniff",
    "X-Frame-Options": "DENY",
    "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
    "Cross-Origin-Resource-Policy": "same-origin",
}


def handler_for(application: PortalApplication) -> Type[BaseHTTPRequestHandler]:
    class PortalHandler(BaseHTTPRequestHandler):
        server_version = "ARRPortal/1.0"

        def do_GET(self) -> None:  # noqa: N802
            self._dispatch(b"")

        def do_POST(self) -> None:  # noqa: N802
            self._dispatch_with_body()

        def do_PATCH(self) -> None:  # noqa: N802
            self._dispatch_with_body()

        def do_DELETE(self) -> None:  # noqa: N802
            self._dispatch_with_body()

        def _dispatch_with_body(self) -> None:
            raw_length = self.headers.get("Content-Length")
            if raw_length is None:
                self._write(
                    Response.json(
                        411,
                        failure(PortalError("CONTENT_LENGTH_REQUIRED", "请求长度缺失", 411)),
                    )
                )
                return
            try:
                length = int(raw_length)
            except ValueError:
                length = -1
            if length < 0 or length > MAX_UPLOAD_BYTES:
                self._write(
                    Response.json(
                        413,
                        failure(PortalError("REQUEST_TOO_LARGE", "请求内容过大", 413)),
                    )
                )
                return
            self._dispatch(self.rfile.read(length))

        def _dispatch(self, body: bytes) -> None:
            response = application.handle(
                self.command,
                self.path,
                {key: value for key, value in self.headers.items()},
                body,
                client_id=str(self.client_address[0]),
            )
            self._write(response)

        def _write(self, response: Response) -> None:
            self.send_response(response.status)
            self.send_header("Content-Type", response.content_type)
            self.send_header("Content-Length", str(len(response.body)))
            for key, value in SECURITY_HEADERS.items():
                self.send_header(key, value)
            for key, value in response.headers.items():
                self.send_header(key, value)
            self.end_headers()
            if self.command != "HEAD":
                self.wfile.write(response.body)

        def log_message(self, format: str, *args: object) -> None:
            # Keep request logs path-only; never print request bodies, cookies, or upload names.
            super().log_message("%s %s", self.command, self.path.split("?", 1)[0])

    return PortalHandler


def serve(application: PortalApplication, host: str, port: int) -> None:
    server = ThreadingHTTPServer((host, port), handler_for(application))
    try:
        server.serve_forever(poll_interval=0.25)
    finally:
        server.server_close()
