from datetime import datetime from typing import List, Optional, Literal from pydantic import BaseModel, Field ShipmentStatus = Literal[ "draft", "submitted", "booked", "in_transit", "delivered", "cancelled", "failed", ] class FedExAddress(BaseModel): recipient_name: str = Field(min_length=2, max_length=150) company_name: Optional[str] = Field(default=None, max_length=150) address_line1: str = Field(min_length=2, max_length=200) address_line2: Optional[str] = Field(default=None, max_length=200) postal_code: str = Field(min_length=2, max_length=20) city: str = Field(min_length=2, max_length=120) country_code: str = Field(min_length=2, max_length=2) phone: Optional[str] = Field(default=None, max_length=50) email: Optional[str] = Field(default=None, max_length=150) class FedExPackageInput(BaseModel): weight_kg: float = Field(gt=0, le=2000) length_cm: float = Field(gt=0, le=400) width_cm: float = Field(gt=0, le=400) height_cm: float = Field(gt=0, le=400) description: str = Field(min_length=1, max_length=255) class FedExBookingCreate(BaseModel): case_id: int = Field(gt=0) customer_id: Optional[int] = Field(default=None, gt=0) contact_id: Optional[int] = Field(default=None, gt=0) service_type: Literal["PRIORITY", "ECONOMY"] = "PRIORITY" pickup_window_start: datetime pickup_window_end: datetime address: FedExAddress packages: List[FedExPackageInput] = Field(min_length=1, max_length=30) class FedExBookingSubmitResponse(BaseModel): booking_ref: str status: ShipmentStatus dry_run: bool tracking_number: Optional[str] = None tracking_url: Optional[str] = None label_url: Optional[str] = None total_amount: Optional[float] = None currency: Optional[str] = None class FedExTrackingEvent(BaseModel): status: str event_timestamp: datetime description: Optional[str] = None location_city: Optional[str] = None location_country: Optional[str] = None class FedExBookingResponse(BaseModel): id: int booking_ref: str case_id: int customer_id: Optional[int] = None contact_id: Optional[int] = None service_type: str shipment_status: ShipmentStatus recipient_name: str city: str country_code: str tracking_number: Optional[str] = None tracking_url: Optional[str] = None label_url: Optional[str] = None total_amount: Optional[float] = None currency: Optional[str] = None dry_run: bool pickup_window_start: datetime pickup_window_end: datetime submitted_at: Optional[datetime] = None created_at: datetime updated_at: datetime packages: List[FedExPackageInput] = Field(default_factory=list) class FedExBookingListResponse(BaseModel): items: List[FedExBookingResponse] class FedExTrackingResponse(BaseModel): booking_ref: str shipment_status: ShipmentStatus tracking_number: Optional[str] = None events: List[FedExTrackingEvent] = Field(default_factory=list) class FedExCancelRequest(BaseModel): reason: Optional[str] = Field(default=None, max_length=400) class FedExCancelResponse(BaseModel): booking_ref: str status: ShipmentStatus cancelled: bool