"""
JSON serialization helpers for API responses.
Ensures numpy/pandas scalars (e.g. np.float64(nan)) are converted so json.dumps does not raise.
"""
import math
from typing import Any

from fastapi.responses import JSONResponse

try:
    from plotly.basedatatypes import BaseFigure as _PlotlyBaseFigure
except ImportError:
    _PlotlyBaseFigure = None  # type: ignore[misc, assignment]


def clean_nan_values(obj: Any) -> Any:
    """
    Recursively make a structure JSON-serializable: replace NaN/Inf with None,
    and convert numpy/pandas scalars to Python types.
    Plotly Figure objects are converted to dicts so FastAPI's encoder never hits
    pydantic isinstance + plotly __getattr__ (RecursionError).
    """
    if _PlotlyBaseFigure is not None and isinstance(obj, _PlotlyBaseFigure):
        return clean_nan_values(obj.to_dict())
    if isinstance(obj, dict):
        return {key: clean_nan_values(value) for key, value in obj.items()}
    if isinstance(obj, list):
        return [clean_nan_values(item) for item in obj]
    if isinstance(obj, float):
        if math.isnan(obj):
            return None
        if math.isinf(obj):
            return None
        return obj
    if hasattr(obj, "item"):
        try:
            v = obj.item()
            if isinstance(v, float) and (math.isnan(v) or math.isinf(v)):
                return None
            return v
        except (ValueError, AttributeError, TypeError):
            return None
    return obj


class SafeJSONResponse(JSONResponse):
    """
    JSONResponse that cleans content (numpy NaN, etc.) before encoding
    so that json.dumps does not raise "Out of range float values are not JSON compliant".
    """

    def __init__(self, content: Any, **kwargs) -> None:
        content = clean_nan_values(content)
        super().__init__(content, **kwargs)
