from __future__ import annotations

from typing import Any

from pydantic import BaseModel, ConfigDict


class BaseRequestSchema(BaseModel):
    """
    Base class for inbound request validation schemas.

    Extra fields are forbidden by default so that callers cannot accidentally
    pass unknown keys::

        class CreateArticleRequest(BaseRequestSchema):
            title: str
            body: str
            tags: list[str] = []
    """

    model_config = ConfigDict(extra="forbid")


class BaseResponseSchema(BaseModel):
    """
    Base class for outbound response serialisation schemas.

    Accepts ORM objects and database rows via ``from_attributes=True``::

        class ArticleResponse(BaseResponseSchema):
            id: int
            title: str
            created_at: datetime
    """

    model_config = ConfigDict(
        from_attributes=True,
        populate_by_name=True,
    )

    def to_dict(self) -> dict[str, Any]:
        """Serialise the response to a plain dict."""
        return self.model_dump()


class BasePatchSchema(BaseModel):
    """
    Base class for partial update (PATCH) schemas.

    All fields are optional by design; only fields explicitly set by the
    caller will be included in ``to_patch_dict()``::

        class PatchArticleRequest(BasePatchSchema):
            title: str | None = None
            body:  str | None = None
    """

    model_config = ConfigDict(extra="forbid")

    def to_patch_dict(self) -> dict[str, Any]:
        """Return only the fields that were explicitly provided."""
        return self.model_dump(exclude_unset=True)
