"""Runtime helpers for generated API base classes.

Provides helper functions used by the generated Tier-2 code:
``_op_meta``, ``_prepare_create_payload``,
``_expand_label_filters``, etc.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any, Mapping

import httpx

from goodmem.errors import raise_for_status


def _op_meta(namespace: Any, op_name: str) -> dict[str, Any]:
    """Look up generated operation metadata for a CRUD method."""
    return (getattr(namespace, "_rest_metadata", {}) or {}).get(op_name, {})


def _supports_label_wildcard(op: dict[str, Any]) -> bool:
    return bool(op.get("supports_label_wildcard")) or "label.*" in (op.get("query_params") or [])


def _expand_label_filters(
    *,
    op: dict[str, Any],
    labels: Mapping[str, str] | None,
    params: dict[str, Any],
) -> dict[str, Any]:
    """Expand labels mapping to literal label.<key> query params when supported."""
    if labels is None:
        return params
    if not _supports_label_wildcard(op):
        raise ValueError("This endpoint does not support label.* query filters.")
    params.update({f"label.{k}": v for k, v in labels.items()})
    return params


def _normalize_multipart_file(file_value: Any) -> Any:
    """Normalize supported file inputs into an httpx files= payload tuple."""
    if isinstance(file_value, tuple):
        return file_value
    if isinstance(file_value, (bytes, bytearray)):
        return ("upload.bin", bytes(file_value), "application/octet-stream")
    if hasattr(file_value, "read"):
        raw_name = getattr(file_value, "name", None) or "upload.bin"
        filename = Path(str(raw_name)).name
        return (filename or "upload.bin", file_value, "application/octet-stream")
    raise TypeError(
        "Multipart file must be a tuple, bytes, bytearray, or a file-like object with read()."
    )


def _prepare_create_payload(
    *,
    create_model: type | None,
    op: dict[str, Any],
    kwargs: dict[str, Any],
) -> tuple[dict[str, Any] | None, dict[str, Any] | None, dict[str, Any] | None]:
    """Build json/data/files payloads for create(), including multipart when declared."""
    supports_multipart = bool(op.get("supports_multipart")) or "multipart/form-data" in (
        op.get("body_kinds") or []
    )
    file_field = op.get("multipart_file_field") or "file"
    request_field = op.get("multipart_request_field") or "request"

    if supports_multipart and file_field in kwargs:
        payload = dict(kwargs)
        raw_file = payload.pop(file_field)
        raw_request = payload.pop(request_field, None)
        if raw_request is None:
            request_kwargs = payload
        elif isinstance(raw_request, dict):
            request_kwargs = raw_request
        elif hasattr(raw_request, "model_dump"):
            request_kwargs = raw_request.model_dump(by_alias=False, exclude_unset=True)
        else:
            raise TypeError(
                f"Multipart '{request_field}' must be a dict or pydantic model, got {type(raw_request)!r}."
            )

        body = create_model.model_validate(request_kwargs) if create_model is not None else None
        request_json = (
            body.model_dump_json(by_alias=True, exclude_unset=True)
            if body is not None
            else "{}"
        )
        data = {request_field: request_json}
        files = {file_field: _normalize_multipart_file(raw_file)}
        return None, data, files

    if create_model is not None:
        body = create_model.model_validate(kwargs)
    else:
        body = None

    if body is not None:
        json_payload = body.model_dump(by_alias=True, exclude_unset=True)
    else:
        json_payload = None
    return json_payload, None, None
