"""Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""

from decimal import Decimal
import json
from typing import Type, TypeVar, Union, get_args
from typing_extensions import get_origin
from pydantic import ConfigDict, create_model
from pydantic_core import from_json
from typing_inspect import is_optional_type

from ..types.basemodel import Nullable


def serialize_decimal(as_str: bool):
    def serialize(d):
        if is_optional_type(type(d)) and d is None:
            return None

        if not isinstance(d, Decimal):
            raise ValueError("Expected Decimal object")

        return str(d) if as_str else float(d)

    return serialize


def validate_decimal(d):
    if d is None:
        return None

    if isinstance(d, Decimal):
        return d

    if not isinstance(d, (str, int, float)):
        raise ValueError("Expected string, int or float")

    return Decimal(str(d))


def serialize_float(as_str: bool):
    def serialize(f):
        if is_optional_type(type(f)) and f is None:
            return None

        if not isinstance(f, float):
            raise ValueError("Expected float")

        return str(f) if as_str else f

    return serialize


def validate_float(f):
    if f is None:
        return None

    if isinstance(f, float):
        return f

    if not isinstance(f, str):
        raise ValueError("Expected string")

    return float(f)


def serialize_int(as_str: bool):
    def serialize(b):
        if is_optional_type(type(b)) and b is None:
            return None

        if not isinstance(b, int):
            raise ValueError("Expected int")

        return str(b) if as_str else b

    return serialize


def validate_int(b):
    if b is None:
        return None

    if isinstance(b, int):
        return b

    if not isinstance(b, str):
        raise ValueError("Expected string")

    return int(b)


def validate_open_enum(is_int: bool):
    def validate(e):
        if e is None:
            return None

        if is_int:
            if not isinstance(e, int):
                raise ValueError("Expected int")
        else:
            if not isinstance(e, str):
                raise ValueError("Expected string")

        return e

    return validate


T = TypeVar("T")


def unmarshal_json(raw, typ: Type[T]) -> T:
    return unmarshal(from_json(raw), typ)


def unmarshal(val, typ: Type[T]) -> T:
    unmarshaller = create_model(
        "Unmarshaller",
        body=(typ, ...),
        __config__=ConfigDict(populate_by_name=True, arbitrary_types_allowed=True),
    )

    m = unmarshaller(body=val)

    return m.body  # pyright: ignore[reportAttributeAccessIssue]


def marshal_json(val, typ):
    if is_nullable(typ) and val is None:
        return "null"

    marshaller = create_model(
        "Marshaller",
        body=(typ, ...),
        __config__=ConfigDict(populate_by_name=True, arbitrary_types_allowed=True),
    )

    m = marshaller(body=val)

    d = m.model_dump(by_alias=True, mode="json", exclude_none=True)

    if len(d) == 0:
        return ""

    return json.dumps(d[next(iter(d))], separators=(",", ":"), sort_keys=True)


def is_nullable(field):
    if get_origin(field) is Nullable:
        return True

    if not get_origin(field) is Union or type(None) not in get_args(field):
        return False

    for arg in get_args(field):
        if get_origin(arg) is Nullable:
            return True

    return False
