from pydantic import BaseModel, Field
from typing import Optional, Literal, Union, List, Dict, Any
from datetime import datetime
from enum import Enum
from bson import ObjectId


# ---------- ENUMS ----------
class DeviceStatus(str, Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    FAULTY = "faulty"


class DeviceType(str, Enum):
    OBDII = "obdii"
    MOBILE = "mobile"
    DASHCAM = "dashcam"
    SEAT_SENSOR = "seat_sensor"
    HEAT_SENSOR = "heat_sensor"
    GPS = "gps"
    OTHERS = "others"


class AssociatedEntityType(str, Enum):
    VEHICLE = "vehicle"
    WORKFORCE = "workforce"


# ---------- BASE MODELS ----------
class IotDeviceBase(BaseModel):
    account_id: str
    user_id: Optional[str] = None
    associated_entity_type: AssociatedEntityType
    associated_entity_id: str
    device_type: DeviceType
    installation_details: str
    status: DeviceStatus
    last_updated: datetime = Field(default_factory=datetime.utcnow)
    model_name: Optional[str] = None
    uuid: Optional[str] = None


class IotDeviceCreate(IotDeviceBase):
    pass


class IotDeviceUpdate(BaseModel):
    user_id: Optional[str] = None
    associated_entity_type: Optional[AssociatedEntityType] = None
    associated_entity_id: Optional[str] = None
    device_type: Optional[DeviceType] = None
    installation_details: Optional[str] = None
    status: Optional[DeviceStatus] = None
    last_updated: Optional[datetime] = Field(default_factory=datetime.utcnow)


class IotDevice(IotDeviceBase):
    id: Optional[str] = None
    created_date: datetime = Field(default_factory=datetime.utcnow)

    # Pydantic v2: use model_config for JSON encoders
    model_config = {
        "json_encoders": {ObjectId: lambda v: str(v)}
    }


class IotDeviceList(BaseModel):
    total_count: int
    devices: List[IotDevice]   # ✅ fixed (was `users`)


# ---------- MAPPED ENTITY DETAILS ----------
class FleetInfo(BaseModel):
    is_fleet_assigned: bool
    assigned_from: Optional[datetime] = None
    assigned_to: Optional[datetime] = None
    status: Optional[bool] = None
    fleet_name: Optional[str] = None
    registration_number: Optional[str] = None
    color: Optional[str] = None
    vehicle_image: Optional[str] = None
    capacity: Optional[str] = None


class FleetMappedDetails(BaseModel):
    type: Literal["fleet"]
    fleet_id: str
    fleet_name: Optional[str] = None
    registration_number: Optional[str] = None
    vehicle_type: Optional[str] = None
    status: Optional[str] = None
    photo: Optional[str] = None
    workforce_info: Optional[Dict[str, Any]] = None
    device_list: Optional[List[Dict[str, Any]]] = None


class WorkforceMappedDetails(BaseModel):
    type: Literal["workforce"]
    workforce_id: str
    first_name: Optional[str] = None
    last_name: Optional[str] = None
    mobile_number: Optional[str] = None
    email_id: Optional[str] = None
    status: Optional[str] = None
    photo: Optional[str] = None
    fleet_info: Optional[FleetInfo] = None
    device_list: Optional[List[Dict[str, Any]]] = None


# ---------- DEVICE WITH EXTRA FIELDS ----------
class IotDeviceWithMapping(IotDevice):
    mapped_details: Optional[Union[FleetMappedDetails, WorkforceMappedDetails]] = None
    distance_km: Optional[float] = None
    last_location: Optional[Dict[str, Any]] = None   # ✅ added so service matches


class IotDeviceWithMappingList(BaseModel):
    total_count: int
    devices: List[IotDeviceWithMapping]


# ---------- REQUEST ----------
class NearbyDevicesRequest(BaseModel):
    latitude: float
    longitude: float
    radius_km: float = 5.0
    skip: int = 0
    limit: int = 10
    list_type: Optional[str] = Field(default="all", pattern="^(all|vehicle|workforce)$")
