"""Common utilities for HTTP handlers."""

from __future__ import annotations

import json
import logging
from typing import TYPE_CHECKING, Any

from core.common import JsonObject

logger = logging.getLogger(__name__)

if TYPE_CHECKING:
    from server.runtime import Runtime

from aiohttp import web

from server.errors import ErrorCode, create_error_response

# aiohttp best practice: use AppKey rather than bare string keys for app state.
# This prevents accidental key collisions and removes NotAppKeyWarning.
RUNTIME_KEY: web.AppKey[Runtime] = web.AppKey("server.runtime")


async def parse_json_body(request: web.Request) -> JsonObject | None:
    """Parse JSON body from request, return None if invalid."""
    try:
        return await request.json()
    except Exception:
        logger.debug("Failed to parse JSON body from request", exc_info=True)
        return None


def get_request_id(request: web.Request) -> str:
    """Get request ID from request."""
    return request.get("request_id", "unknown")


def error_response(
    request: web.Request,
    error: str,
    code: ErrorCode,
    status: int = 400,
    details: JsonObject | None = None,
) -> web.Response:
    """Create standardized error response."""
    return json_response(
        request,
        create_error_response(
            error=error,
            code=code,
            request_id=get_request_id(request),
            details=details,
        ),
        status=status,
    )


def _pretty_json_dumps(obj: Any) -> str:
    # Stable, human-friendly JSON for terminal/browser usage.
    return json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False)


def json_response(
    request: web.Request,
    data: dict[str, Any],
    *,
    status: int = 200,
    headers: dict[str, str] | None = None,
) -> web.Response:
    """Return JSON, optionally pretty-printed via ``?pretty=1``.

    Callers are responsible for passing JSON-safe dicts (use
    ``model.model_dump(mode="json")`` for Pydantic models).
    """
    pretty = str(request.query.get("pretty", "") or "").strip().lower() in {
        "1",
        "true",
        "yes",
        "on",
    }

    if pretty:
        return web.json_response(data, status=status, headers=headers, dumps=_pretty_json_dumps)
    return web.json_response(data, status=status, headers=headers)


def get_runtime(request: web.Request) -> Runtime:
    """Get runtime from app state."""
    return request.app[RUNTIME_KEY]


__all__ = [
    "RUNTIME_KEY",
    "error_response",
    "get_request_id",
    "get_runtime",
    "json_response",
    "parse_json_body",
]
