"""
JSON response classes for common HTTP status codes.

Each class is a `JsonResponse <https://docs.djangoproject.com/en/stable/ref/request-response/#django.http.JsonResponse>`_ subclass that uses the
given status code and encodes the first argument as JSON (Content-Type:
application/json). Use these for JSON APIs when you want both status and
body in one call.

Example::

    from django_allresponses.jsonresponses import (
        JsonResponseOK,
        JsonResponseBadRequest,
        JsonResponseNotFound,
    )

    def my_api_view(request):
        if not request.user.is_authenticated:
            return JsonResponseUnauthorized({"error": "Authentication required"})
        if not valid_input(request.POST):
            return JsonResponseBadRequest({"errors": ["Invalid input"]})
        obj = get_object_or_None(id=request.GET.get("id"))
        if obj is None:
            return JsonResponseNotFound({"error": "Resource not found"})
        return JsonResponseOK({"id": obj.id, "name": obj.name})
"""

from __future__ import annotations

from typing import Any, Iterable

from django.http import JsonResponse


class _JsonResponseWithStatus(JsonResponse):
    """Base for JSON responses that fix status_code; subclasses set status_code."""

    status_code = 200

    def __init__(self, data: Any, **kwargs: Any) -> None:
        kwargs.setdefault("status", self.status_code)
        super().__init__(data, **kwargs)


# ---------------------------------------------------------------------------
# 2xx Success
# ---------------------------------------------------------------------------


class JsonResponseOK(_JsonResponseWithStatus):
    """200 OK with JSON body."""

    status_code = 200


class JsonResponseCreated(_JsonResponseWithStatus):
    """201 Created with JSON body and Location header."""

    status_code = 201

    def __init__(self, data: Any = None, *, location: str, **kwargs: Any) -> None:
        if data is None:
            data = {}
        kwargs.setdefault("status", self.status_code)
        super().__init__(data, **kwargs)
        self["Location"] = location


# ---------------------------------------------------------------------------
# 4xx Client Error
# ---------------------------------------------------------------------------


class JsonResponseBadRequest(_JsonResponseWithStatus):
    """400 Bad Request with JSON body."""

    status_code = 400


class JsonResponseUnauthorized(_JsonResponseWithStatus):
    """401 Unauthorized with JSON body."""

    status_code = 401


class JsonResponsePaymentRequired(_JsonResponseWithStatus):
    """402 Payment Required with JSON body."""

    status_code = 402


class JsonResponseForbidden(_JsonResponseWithStatus):
    """403 Forbidden with JSON body."""

    status_code = 403


class JsonResponseNotFound(_JsonResponseWithStatus):
    """404 Not Found with JSON body."""

    status_code = 404


class JsonResponseNotAllowed(_JsonResponseWithStatus):
    """405 Method Not Allowed with JSON body and Allow header."""

    status_code = 405

    def __init__(
        self,
        data: Any = None,
        *,
        permitted_methods: Iterable[str],
        **kwargs: Any,
    ) -> None:
        if data is None:
            data = {}
        kwargs.setdefault("status", self.status_code)
        super().__init__(data, **kwargs)
        self["Allow"] = ", ".join(str(m).upper() for m in permitted_methods)


class JsonResponseNotAcceptable(_JsonResponseWithStatus):
    """406 Not Acceptable with JSON body."""

    status_code = 406


class JsonResponseConflict(_JsonResponseWithStatus):
    """409 Conflict with JSON body."""

    status_code = 409


class JsonResponseUnprocessableContent(_JsonResponseWithStatus):
    """422 Unprocessable Content with JSON body (e.g. validation errors)."""

    status_code = 422


# ---------------------------------------------------------------------------
# 5xx Server Error
# ---------------------------------------------------------------------------


class JsonResponseServerError(_JsonResponseWithStatus):
    """500 Internal Server Error with JSON body."""

    status_code = 500


class JsonResponseServiceUnavailable(_JsonResponseWithStatus):
    """503 Service Unavailable with JSON body."""

    status_code = 503
