from typing import Any, Optional, Union

from pydantic import BaseModel, ConfigDict, Field

_IGNORE = ConfigDict(extra="ignore")
_IGNORE_POPULATE = ConfigDict(extra="ignore", populate_by_name=True)


class Vec3(BaseModel):
    model_config = _IGNORE
    x: float
    y: float
    z: float


class Symbol(BaseModel):
    model_config = _IGNORE
    id: str
    name: str
    description: Optional[str] = None
    valid: bool


class FieldValue(BaseModel):
    model_config = _IGNORE
    index: int
    has_value: bool
    is_array: bool
    value: Union[str, list[str]]


class SurveyField(BaseModel):
    model_config = _IGNORE
    id: str
    type: int
    type_as_string: str
    is_required: bool
    label: str
    description: Optional[str] = None
    list_values: list[str] = []
    value: FieldValue


class ImageMapEntry(BaseModel):
    model_config = _IGNORE
    symbol_id: str
    uid: str
    owner_uid: str
    type: int
    coordinates: list[int]


class FormattedDimensions(BaseModel):
    model_config = _IGNORE
    width: Optional[str] = None
    depth: Optional[str] = None
    height: Optional[str] = None


class FormattedMeasures(BaseModel):
    model_config = _IGNORE
    width: Optional[str] = None
    depth: Optional[str] = None
    height: Optional[str] = None
    area: Optional[str] = None
    area_without_walls: Optional[str] = None
    area_with_interior_walls_only: Optional[str] = None
    area_with_walls: Optional[str] = None
    doors_surface: Optional[str] = None
    walls_surface: Optional[str] = None
    walls_surface_without_openings: Optional[str] = None
    windows_surface: Optional[str] = None
    perimeter: Optional[str] = None
    ground_perimeter: Optional[str] = None
    living_area: Optional[str] = None
    below_grade_living_area: Optional[str] = None
    above_grade_living_area: Optional[str] = None
    exterior_perimeter: Optional[str] = None
    volume: Optional[str] = None


class Address(BaseModel):
    model_config = _IGNORE
    street: Optional[str] = None
    street_number: Optional[str] = None
    postal_code: Optional[str] = None
    city: Optional[str] = None
    country: Optional[str] = None
    longitude: Optional[float] = None
    latitude: Optional[float] = None


class CreatedBy(BaseModel):
    model_config = _IGNORE
    id: str
    email: str
    firstname: Optional[str] = None
    lastname: Optional[str] = None


class Location(BaseModel):
    model_config = _IGNORE
    valid: bool
    longitude: float
    latitude: float
    altitude: float


class ItemBase(BaseModel):
    model_config = _IGNORE
    uid: str
    symbol: Symbol
    size: Vec3
    position: Vec3
    rotation: Vec3
    formatted: Optional[FormattedDimensions] = None
    images: list[Any] = []
    notes: Optional[str] = None
    displayable_fields: list[SurveyField] = []
    custom_displayable_fields: list[SurveyField] = []


class WallItem(ItemBase):
    pass


class Furniture(ItemBase):
    pass


class SymbolInstance(ItemBase):
    pass


class Wall(BaseModel):
    model_config = _IGNORE
    uid: str
    symbol: Symbol
    length: float
    images: list[Any] = []
    notes: Optional[str] = None
    displayable_fields: list[SurveyField] = []
    custom_displayable_fields: list[SurveyField] = []


class Room(BaseModel):
    model_config = _IGNORE
    name: str
    uid: str
    symbol: Optional[Symbol] = None
    size: Vec3
    position: Vec3
    rotation: Vec3
    area: float
    perimeter: Optional[float] = None
    ground_perimeter: Optional[float] = None
    area_without_walls: Optional[float] = None
    area_with_interior_walls_only: Optional[float] = None
    area_with_walls: Optional[float] = None
    wall_count: Optional[int] = None
    wall_count_with_interior_walls: Optional[int] = None
    corner_count_with_interior_walls: Optional[int] = None
    door_count: Optional[int] = None
    window_count: Optional[int] = None
    height: Optional[float] = None
    volume: Optional[float] = None
    width: Optional[float] = None
    doors_surface: Optional[float] = None
    walls_surface: Optional[float] = None
    walls_surface_without_openings: Optional[float] = None
    windows_surface: Optional[float] = None
    dimensions: Optional[str] = None
    room_type: Optional[str] = None
    furniture_count: Optional[int] = None
    image: Optional[str] = None
    image_map: list[ImageMapEntry] = []
    images: list[Any] = []
    notes: Optional[str] = None
    formatted: Optional[FormattedMeasures] = None
    displayable_fields: list[SurveyField] = []
    custom_displayable_fields: list[SurveyField] = []
    wall_items: list[WallItem] = []
    furnitures: list[Furniture] = []
    walls: list[Wall] = []


class Floor(BaseModel):
    model_config = _IGNORE
    uid: str
    symbol: Optional[Symbol] = None
    size: Vec3
    position: Vec3
    rotation: Vec3
    name: Optional[str] = None
    area: Optional[float] = None
    perimeter: Optional[float] = None
    ground_perimeter: Optional[float] = None
    area_without_walls: Optional[float] = None
    area_with_interior_walls_only: Optional[float] = None
    area_with_walls: Optional[float] = None
    wall_count: Optional[int] = None
    wall_count_with_interior_walls: Optional[int] = None
    corner_count_with_interior_walls: Optional[int] = None
    door_count: Optional[int] = None
    window_count: Optional[int] = None
    bathrooms_count: Optional[int] = None
    bedrooms_count: Optional[int] = None
    doors_surface: Optional[float] = None
    floor_type: Optional[Union[int, str]] = None
    furniture_count: Optional[int] = None
    height: Optional[float] = None
    level: Optional[int] = None
    room_count: Optional[int] = None
    volume: Optional[float] = None
    walls_surface: Optional[float] = None
    walls_surface_without_openings: Optional[float] = None
    windows_surface: Optional[float] = None
    image: Optional[str] = None
    image_map: list[ImageMapEntry] = []
    images: list[Any] = []
    notes: Optional[str] = None
    formatted: Optional[FormattedMeasures] = None
    displayable_fields: list[SurveyField] = []
    custom_displayable_fields: list[SurveyField] = []
    rooms: list[Room] = []
    furnitures: list[Furniture] = []
    symbol_instances: list[SymbolInstance] = []


class PlanBody(BaseModel):
    model_config = _IGNORE
    uid: str
    name: Optional[str] = None
    symbol: Optional[Symbol] = None
    size: Vec3
    position: Vec3
    rotation: Vec3
    area: Optional[float] = None
    location: Location
    floors: list[Floor] = []
    images: list[Any] = []
    notes: Optional[str] = None
    formatted: Optional[FormattedMeasures] = None
    displayable_fields: list[SurveyField] = []
    custom_displayable_fields: list[SurveyField] = []
    customer: list[Any] = []
    custom_attributes: list[Any] = []


class PlanDetail(BaseModel):
    model_config = _IGNORE
    extension_version: Optional[str] = None
    wrapper_version: Optional[str] = None
    document_version: Optional[str] = None
    last_modification_date: Optional[Union[int, str]] = None
    plan: PlanBody


class PlanSummary(BaseModel):
    model_config = _IGNORE_POPULATE
    id: str
    project_id: Optional[str] = None
    name: str
    address: Optional[Address] = None
    creation_date: Optional[str] = None
    update_date: Optional[str] = None
    thumbnail_url: Optional[str] = None
    public_url: Optional[str] = None
    cloud_url: Optional[str] = None
    url_3d: Optional[str] = Field(default=None, alias="3d_url")
    workgroup_id: Optional[str] = None
    team_id: Optional[str] = None
    created_by: Optional[CreatedBy] = None


class Paging(BaseModel):
    model_config = _IGNORE
    page: int
    next_page: bool
    count: int


class PlansListResponse(BaseModel):
    model_config = _IGNORE
    paging: Paging
    plans: list[PlanSummary] = []


class MagicPlanPlan(BaseModel):
    model_config = _IGNORE
    plan: PlanSummary
    plan_detail: PlanDetail
