"""Public request and response models: camelCase on the wire, separate models per direction."""
from datetime import datetime
from typing import Literal

from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, field_validator
from pydantic.alias_generators import to_camel

OrderStatus = Literal["pending", "paid", "preparing", "ready", "completed", "cancelled"]


class ApiModel(BaseModel):
    model_config = ConfigDict(alias_generator=to_camel, validate_by_name=True, from_attributes=True)


class InputModel(ApiModel):
    model_config = ConfigDict(extra="forbid")        # unknown fields are errors: no mass assignment


class ProductOut(ApiModel):
    sku: str
    name: str
    price_minor: int


class OrderItemIn(InputModel):
    sku: str = Field(min_length=1, max_length=40)
    quantity: int = Field(ge=1, le=20)


class OrderIn(InputModel):
    items: list[OrderItemIn] = Field(min_length=1, max_length=20)
    note: str | None = Field(default=None, max_length=200)

    @field_validator("items")
    @classmethod
    def _unique_skus(cls, items: list[OrderItemIn]) -> list[OrderItemIn]:
        skus = [item.sku for item in items]
        if len(skus) != len(set(skus)):
            raise ValueError("each sku may appear only once; use quantity")
        return items


class OrderPatch(InputModel):
    note: str | None = Field(default=None, max_length=200)


class StatusChange(InputModel):
    status: OrderStatus


class OrderItemOut(ApiModel):
    sku: str
    name: str
    unit_price_minor: int
    quantity: int


class OrderOut(ApiModel):
    id: int
    customer_id: int
    status: OrderStatus
    note: str | None
    currency: str
    total_minor: int
    version: int
    created_at: AwareDatetime
    updated_at: AwareDatetime
    items: list[OrderItemOut]


class OrderPage(ApiModel):
    items: list[OrderOut]
    next_cursor: str | None


class StatusEvent(ApiModel):
    order_id: int
    status: OrderStatus
    version: int
    updated_at: datetime


class UserOut(ApiModel):
    id: int
    email: str
    role: str


class TokenOut(BaseModel):
    """OAuth2 token responses use snake_case field names by specification."""

    access_token: str
    token_type: Literal["bearer"] = "bearer"
    expires_in: int


class Problem(BaseModel):
    """RFC 9457 problem details (for the OpenAPI document)."""

    type: str = "about:blank"
    title: str
    status: int
    detail: str | None = None
    instance: str | None = None
    request_id: str | None = None
