"""Pydantic schemas for vehicle-related request and response bodies.""" import uuid from datetime import date, datetime from decimal import Decimal from typing import Literal, Optional from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator VEHICLE_TYPES = Literal["lkw", "pkw", "baumaschine", "stapler", "transporter"] CONDITIONS = Literal["new", "used"] AVAILABILITY = Literal["available", "reserved", "sold"] HOURS_UNITS = Literal["h", "min"] class VehicleBase(BaseModel): """Base vehicle fields shared across schemas.""" make: str = Field(..., min_length=1, max_length=100) model: str = Field(..., min_length=1, max_length=100) fin: str = Field(..., min_length=17, max_length=17) year: Optional[int] = Field(None, ge=1900, le=2100) first_registration: Optional[date] = None power_kw: Optional[int] = Field(None, ge=0) power_hp: Optional[int] = Field(None, ge=0) fuel_type: Optional[str] = Field(None, max_length=50) transmission: Optional[str] = Field(None, max_length=20) color: Optional[str] = Field(None, max_length=50) condition: CONDITIONS = "used" location: Optional[str] = Field(None, max_length=255) availability: AVAILABILITY = "available" price: Decimal = Field(..., ge=0) vehicle_type: VEHICLE_TYPES lkw_type: Optional[str] = Field(None, max_length=50) machine_type: Optional[str] = Field(None, max_length=50) body_type: Optional[str] = Field(None, max_length=100) operating_hours: Optional[Decimal] = Field(None, ge=0) operating_hours_unit: Optional[HOURS_UNITS] = None mileage_km: Optional[int] = Field(None, ge=0) description: Optional[str] = None @model_validator(mode="after") def compute_power_hp(self) -> "VehicleBase": """Auto-compute power_hp from power_kw if not provided.""" if self.power_kw is not None and self.power_hp is None: self.power_hp = round(self.power_kw * 1.35962) return self class VehicleCreate(VehicleBase): """POST /api/v1/vehicles request body.""" pass class VehicleUpdate(BaseModel): """PUT /api/v1/vehicles/:id request body (all fields optional).""" make: Optional[str] = Field(None, min_length=1, max_length=100) model: Optional[str] = Field(None, min_length=1, max_length=100) fin: Optional[str] = Field(None, min_length=17, max_length=17) year: Optional[int] = Field(None, ge=1900, le=2100) first_registration: Optional[date] = None power_kw: Optional[int] = Field(None, ge=0) power_hp: Optional[int] = Field(None, ge=0) fuel_type: Optional[str] = Field(None, max_length=50) transmission: Optional[str] = Field(None, max_length=20) color: Optional[str] = Field(None, max_length=50) condition: Optional[CONDITIONS] = None location: Optional[str] = Field(None, max_length=255) availability: Optional[AVAILABILITY] = None price: Optional[Decimal] = Field(None, ge=0) vehicle_type: Optional[VEHICLE_TYPES] = None lkw_type: Optional[str] = Field(None, max_length=50) machine_type: Optional[str] = Field(None, max_length=50) body_type: Optional[str] = Field(None, max_length=100) operating_hours: Optional[Decimal] = Field(None, ge=0) operating_hours_unit: Optional[HOURS_UNITS] = None mileage_km: Optional[int] = Field(None, ge=0) description: Optional[str] = None @model_validator(mode="after") def compute_power_hp(self) -> "VehicleUpdate": """Auto-compute power_hp from power_kw if not provided.""" if self.power_kw is not None and self.power_hp is None: self.power_hp = round(self.power_kw * 1.35962) return self class VehicleResponse(BaseModel): """Vehicle response schema.""" model_config = ConfigDict(from_attributes=True) id: uuid.UUID make: str model: str fin: str year: Optional[int] = None first_registration: Optional[date] = None power_kw: Optional[int] = None power_hp: Optional[int] = None fuel_type: Optional[str] = None transmission: Optional[str] = None color: Optional[str] = None condition: str location: Optional[str] = None availability: str price: Decimal vehicle_type: str lkw_type: Optional[str] = None machine_type: Optional[str] = None body_type: Optional[str] = None operating_hours: Optional[Decimal] = None operating_hours_unit: Optional[str] = None mileage_km: Optional[int] = None description: Optional[str] = None created_at: Optional[datetime] = None updated_at: Optional[datetime] = None deleted_at: Optional[datetime] = None class VehicleListResponse(BaseModel): """Paginated vehicle list response.""" items: list[VehicleResponse] total: int page: int page_size: int class MobileDeStatusResponse(BaseModel): """mobile.de sync status for a vehicle.""" synced: bool ad_id: Optional[str] = None synced_at: Optional[datetime] = None sync_status: str = "pending" error_log: Optional[str] = None class MobileDePushResponse(BaseModel): """Response for mobile.de push request.""" message: str = "Push queued" vehicle_id: uuid.UUID listing_id: Optional[uuid.UUID] = None