from enum import Enum
from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
from typing import Any, FrozenSet, Generic, TypeVar

from django.core.serializers.json import DjangoJSONEncoder
from django.http import JsonResponse
from pydantic import AnyUrl, BaseModel
from pydantic_core import Url

__all__ = [
    "NinjaJSONEncoder",
    "Response",
    "Status",
    "codes_1xx",
    "codes_2xx",
    "codes_3xx",
    "codes_4xx",
    "codes_5xx",
]


T = TypeVar("T")


class Status(Generic[T]):
    """Return a response with an explicit HTTP status code.

    Usage:
        return Status(200, {"key": "value"})
        return Status(204, None)
    """

    __slots__ = ("status_code", "value")

    def __init__(self, status_code: int, value: T):
        self.status_code = status_code
        self.value = value


class NinjaJSONEncoder(DjangoJSONEncoder):
    def default(self, o: Any) -> Any:
        if isinstance(o, BaseModel):
            return o.model_dump()
        if isinstance(o, (Url, AnyUrl)):
            return str(o)
        if isinstance(o, (IPv4Address, IPv4Network, IPv6Address, IPv6Network)):
            return str(o)
        if isinstance(o, Enum):
            return str(o)
        return super().default(o)


class Response(JsonResponse):
    def __init__(self, data: Any, **kwargs: Any) -> None:
        super().__init__(data, encoder=NinjaJSONEncoder, safe=False, **kwargs)


def resp_codes(from_code: int, to_code: int) -> FrozenSet[int]:
    return frozenset(range(from_code, to_code + 1))


# most common http status codes
codes_1xx = resp_codes(100, 101)
codes_2xx = resp_codes(200, 206)
codes_3xx = resp_codes(300, 308)
codes_4xx = resp_codes(400, 412) | frozenset({416, 418, 425, 429, 451})
codes_5xx = resp_codes(500, 504)
