import json
from dataclasses import asdict, is_dataclass
from functools import partial
from pprint import pformat
from typing import Any

import numpy


def _dict_factory_nonone(obj: Any):
    """dict factory with items with None value ignored"""
    return {key: value for key, value in obj if value is not None}


def _obj_encode(obj: Any, *, abrv: bool = False):
    if is_dataclass(obj) and not isinstance(obj, type):
        return asdict(obj, dict_factory=_dict_factory_nonone)
    if isinstance(obj, numpy.dtype):
        return obj.name
    if isinstance(obj, (numpy.ndarray, numpy.ma.MaskedArray)):
        if abrv:
            # https://numpy.org/doc/stable/reference/generated/numpy.array2string.html
            data = numpy.array2string(
                obj,
                max_line_width=1000000,
                separator=", ",
                threshold=16,
                formatter={
                    "all": str,
                },
            )
            return f"array(data={data}, dtype={obj.dtype})"
        return obj.tolist()
    return f"??{type(obj)}??"


def formatter(obj: Any, format: str = "str"):  # noqa: A002
    """format an object for printing to the screen

    See FORMAT_OPTIONS for available formats.  If format is not
    specified the str representation of the object is returned.

    """
    if format == "str":
        return str(obj)
    if format == "repr":
        return repr(obj)
    if format == "pprint":
        return pformat(obj)
    if format == "json":
        return json.dumps(obj, default=_obj_encode)
    if format == "json-pp":
        return json.dumps(obj, default=_obj_encode, indent=4)
    if format == "json-abrv":
        return json.dumps(
            obj,
            default=partial(_obj_encode, abrv=True),
            indent=4,
        )
    msg = f"Unknown format: {format}"
    raise ValueError(msg)


FORMAT_OPTIONS = {
    "repr": "repr",
    "pprint": "pprint",
    "json": "JSON, one entry per line",
    "json-pp": "JSON, human-readable formatting",
    "json-abrv": "json-pp but with arrays abreviated by their repr (non-valid JSON)",
}
