from __future__ import annotations

from typing import Any

from pydantic import AliasChoices, BaseModel, ConfigDict, Field, RootModel, model_validator


class StatusMessage(BaseModel):
    """Basic status/message response payload."""

    model_config = ConfigDict(extra="allow")

    status: int | None = None
    message: str | None = None


class EsResponse(BaseModel):
    """Elasticsearch proxy response payload."""

    model_config = ConfigDict(extra="allow")

    statusCode: int
    body: Any


class QueryResponse(BaseModel):
    """Query response payload."""

    model_config = ConfigDict(extra="allow")

    status: int
    resp: EsResponse


class CreditResponse(BaseModel):
    """Credits response payload."""

    model_config = ConfigDict(extra="allow")

    status: int | None = None
    message: str | None = None
    current_balance: int | None = None


class UserInfo(BaseModel):
    """User profile detail payload."""

    model_config = ConfigDict(extra="allow")

    name: str
    age: int


class UserProfileResponse(BaseModel):
    """User profile response payload."""

    model_config = ConfigDict(extra="allow")

    status: int
    message: str
    user: UserInfo


class VersionResponse(BaseModel):
    """Version response payload."""

    model_config = ConfigDict(extra="allow")

    status: int
    version: str


class QueryRequest(RootModel[dict[str, Any]]):
    """Request payload for the query endpoint."""

    @model_validator(mode="after")
    def validate_query_key(self) -> QueryRequest:
        """Ensure required query key exists."""
        if "query" not in self.root:
            raise ValueError("query field is required")
        return self


class FaceSearchRequest(BaseModel):
    """Request payload for the face search endpoint."""

    model_config = ConfigDict(extra="allow", populate_by_name=True)

    image_b64: str | None = Field(
        default=None,
        min_length=20,
        validation_alias=AliasChoices("image_b64", "image_base64"),
        serialization_alias="image_b64",
    )
    embedding: list[float] | None = None
    top_k: int = Field(
        default=5,
        ge=1,
        le=100,
        validation_alias=AliasChoices("top_k", "max_k", "limit"),
        serialization_alias="top_k",
    )
    confidence: float | None = Field(default=None, ge=0)
    min_score: float | None = Field(default=None, ge=0)

    @model_validator(mode="after")
    def validate_face_search_input(self) -> FaceSearchRequest:
        """Ensure a face-search request contains either image bytes or an embedding."""
        if not self.image_b64 and self.embedding is None:
            raise ValueError("face search requires image_b64 or embedding")
        if self.embedding is not None and len(self.embedding) != 512:
            raise ValueError("embedding must contain exactly 512 dimensions")
        return self
