from fastapi import HTTPException
from app.v1.models.platform.iotdevices import IotDeviceCreate, IotDeviceUpdate,NearbyDevicesRequest
from app.v1.libraries.object import str_to_objectid
from datetime import datetime
from pymongo import ASCENDING, DESCENDING
from typing import Optional
from bson import ObjectId

COLLECTION_NAME = "iot_devices"

def safe_objectid(id_str: str):
    """Return ObjectId if valid 24-hex string, else None (instead of raising 400)."""
    if not id_str or not isinstance(id_str, str):
        return None
    try:
        return ObjectId(id_str)
    except Exception:
        return None

# async def create_iot_device_service(device: IotDeviceCreate, db):
#     data = device.dict()
#     data["created_date"] = datetime.utcnow()
#     result = db[COLLECTION_NAME].insert_one(data)
#     data["id"] = str(result.inserted_id)
#     return data

async def create_iot_device_service(device: IotDeviceCreate, db):
    data = device.dict()
    data["created_date"] = datetime.utcnow()

    # Determine the collection and fetch tag_name
    tag_name = None
    if data["associated_entity_type"] == "workforce":
        entity = db["workforce"].find_one({"_id": ObjectId(data["associated_entity_id"])})        
        if entity:
            tag_name = entity.get("tag_name")
            first_name = entity.get("first_name")
            last_name = entity.get("last_name")
            mobile_number = entity.get("mobile_number")
            email_id = entity.get("email_id")
            data["first_name"] = first_name  
            data["last_name"] = last_name  
            data["mobile_number"] = mobile_number  
            data["email_id"] = email_id 
    elif data["associated_entity_type"] == "vehicle":
        entity = db["fleets"].find_one({"_id": ObjectId(data["associated_entity_id"])})         
        if entity:
            tag_name = entity.get("tag_name")
            fleet_name = entity.get("fleet_name")
            registration_number = entity.get("registration_number")
            vin_number = entity.get("vin_number")
            data["fleet_name"] = fleet_name  
            data["registration_number"] = registration_number  
            data["vin_number"] = vin_number   

    data["tag_name"] = tag_name  # Add tag_name to IoT device 

    result = db["iot_devices"].insert_one(data)
    data["id"] = str(result.inserted_id)
    return data


async def get_iot_device_service(account_id: str, db):
    device = db[COLLECTION_NAME].find_one({"_id": str_to_objectid(account_id)})
    if not device:
        raise HTTPException(status_code=404, detail="IoT Device not found")
    device["id"] = str(device["_id"])
    return device


# async def update_iot_device_service(account_id: str, update: IotDeviceUpdate, db):
#     update_data = {k: v for k, v in update.dict(exclude_unset=True).items()}
#     update_data["last_updated"] = datetime.utcnow()
#     await db[COLLECTION_NAME].update_one({"_id": str_to_objectid(account_id)}, {"$set": update_data})
#     return await get_iot_device_service(account_id, db)

async def update_iot_device_service(account_id: str, update: IotDeviceUpdate, db):
    update_data = {k: v for k, v in update.dict(exclude_unset=True).items()}
    update_data["last_updated"] = datetime.utcnow()
    await db[COLLECTION_NAME].update_one(
        {"_id": str_to_objectid(account_id)}, {"$set": update_data}
    )

    # 🔔 Notify WebSocket subscribers
    from app.v1.sockets.tracking_data import notify_nearby_subscribers
    await notify_nearby_subscribers(account_id)

    return await get_iot_device_service(account_id, db)


async def delete_iot_device_service(account_id: str, db):
    device = await get_iot_device_service(account_id, db)
    await db[COLLECTION_NAME].delete_one({"_id": str_to_objectid(account_id)})
    return device

async def list_iot_devices_service(skip, limit, q: Optional[str], device_type, status, date_from, date_to, db):
    filter_query = {}

    if q:
        regex_query = {"$regex": q, "$options": "i"}
        filter_query["$or"] = [
            {"model_name": regex_query},
            {"installation_details": regex_query}
        ]

    if device_type:
        filter_query["device_type"] = device_type

    if status:
        filter_query["status"] = status

    if date_from or date_to:
        date_filter = {}
        if date_from:
            date_filter["$gte"] = datetime.strptime(date_from, "%Y-%m-%d")
        if date_to:
            date_filter["$lte"] = datetime.strptime(date_to, "%Y-%m-%d")
        filter_query["created_date"] = date_filter

    cursor = db[COLLECTION_NAME].find(filter_query).skip(skip).limit(limit)

    devices = []
    for d in cursor:
        d["id"] = str(d["_id"])
        devices.append(d)

    total = db[COLLECTION_NAME].count_documents(filter_query)

    # ✅ Corrected here
    return {"total_count": total, "devices": devices}

# async def list_iot_devices_with_mapping_service(
#     skip: int,
#     limit: int,
#     q: Optional[str],
#     device_type: Optional[str],
#     status: Optional[str],
#     date_from: Optional[str],
#     date_to: Optional[str],
#     db
# ):
#     filter_query = {}

#     if q:
#         regex_query = {"$regex": q, "$options": "i"}
#         filter_query["$or"] = [
#             {"model_name": regex_query},
#             {"installation_details": regex_query}
#         ]

#     if device_type:
#         filter_query["device_type"] = device_type

#     if status:
#         filter_query["status"] = status

#     if date_from or date_to:
#         date_filter = {}
#         if date_from:
#             date_filter["$gte"] = datetime.strptime(date_from, "%Y-%m-%d")
#         if date_to:
#             date_filter["$lte"] = datetime.strptime(date_to, "%Y-%m-%d")
#         filter_query["created_date"] = date_filter

#     cursor = db["iot_devices"].find(filter_query).skip(skip).limit(limit)

#     devices = []
#     for d in cursor:
#         d["id"] = str(d["_id"])
#         associated_type = d.get("associated_entity_type")
#         associated_id = d.get("associated_entity_id")

#         mapped_data = None

#         if associated_type == "vehicle":
#             fleet = db["fleets"].find_one({"_id": str_to_objectid(associated_id)})
#             if fleet:
#                 mapped_data = {
#                     "type": "fleet",
#                     "fleet_id": str(fleet["_id"]),
#                     "fleet_name": fleet.get("fleet_name"),
#                     "registration_number": fleet.get("registration_number"),
#                     "vehicle_type": fleet.get("vehicle_type"),
#                     "status": fleet.get("status"),
#                 }

#         elif associated_type == "workforce":
#             workforce = db["workforce"].find_one({"_id": str_to_objectid(associated_id)})
#             if workforce:
#                 mapped_data = {
#                     "type": "workforce",
#                     "workforce_id": str(workforce["_id"]),
#                     "first_name": workforce.get("first_name"),
#                     "last_name": workforce.get("last_name"),
#                     "mobile_number": workforce.get("mobile_number"),
#                     "email_id": workforce.get("email_id"),
#                     "status": workforce.get("status"),
#                 }

#         d["mapped_details"] = mapped_data
#         devices.append(d)

#     total = db["iot_devices"].count_documents(filter_query)
#     return {"total_count": total, "devices": devices}

# def list_iot_devices_with_mapping_service(
#     skip: int,
#     limit: int,
#     q: Optional[str],
#     device_type: Optional[str],
#     status: Optional[str],
#     date_from: Optional[str],
#     date_to: Optional[str],
#     associated_entity_type: Optional[str],
#     db
# ):
#     filter_query = {}
    
#     if q:
#         if q.startswith("#"):
#             # 🔍 Search by tag_name when q starts with #
#             tag_val = q[1:]  # remove the #
#             regex_query = {"$regex": tag_val, "$options": "i"}
#             filter_query["tag_name"] = regex_query
#         else:
#             # 🔍 Default search by model_name or installation_details
#             regex_query = {"$regex": q, "$options": "i"}
#             filter_query["$or"] = [
#                 {"model_name": regex_query},
#                 {"installation_details": regex_query}
#             ]

#     if device_type:
#         filter_query["device_type"] = device_type

#     if status:
#         filter_query["status"] = status

#     if associated_entity_type:   # ✅ filter by workforce / vehicle
#         filter_query["associated_entity_type"] = associated_entity_type

#     if date_from or date_to:
#         date_filter = {}
#         if date_from:
#             date_filter["$gte"] = datetime.strptime(date_from, "%Y-%m-%d")
#         if date_to:
#             date_filter["$lte"] = datetime.strptime(date_to, "%Y-%m-%d")
#         filter_query["created_date"] = date_filter

#     cursor = db["iot_devices"].find(filter_query).skip(skip).limit(limit)

#     devices = []
#     for d in cursor:
#         d["id"] = str(d["_id"])
#         associated_type = d.get("associated_entity_type")
#         associated_id = d.get("associated_entity_id")

#         mapped_data = None
#         oid = safe_objectid(associated_id)   # ✅ never raises

#         # if associated_entity_type == "vehicle" and oid:
#         #     fleet = db["fleets"].find_one({"_id": oid})
#         #     if fleet:
#         #         mapped_data = {
#         #             "type": "fleet",
#         #             "fleet_id": str(fleet["_id"]),
#         #             "fleet_name": fleet.get("fleet_name"),
#         #             "registration_number": fleet.get("registration_number"),
#         #             "vehicle_type": fleet.get("vehicle_type"),
#         #             "status": fleet.get("status"),
#         #             "photo": fleet.get("vehicle_image"),
#         #         }
#         if associated_entity_type == "vehicle" and oid:
#             fleet = db["fleets"].find_one({"_id": oid})
#             mapped_data = None

#             if fleet:
#                 # Base vehicle mapping
#                 mapped_data = {
#                     "type": "fleet",
#                     "fleet_id": str(fleet["_id"]),
#                     "fleet_name": fleet.get("fleet_name"),
#                     "registration_number": fleet.get("registration_number"),
#                     "vehicle_type": fleet.get("vehicle_type"),
#                     "status": fleet.get("status"),
#                     "photo": fleet.get("vehicle_image"),
#                 }

#                 # 🔍 Check if this vehicle has a mapped workforce
#                 wf_vehicle = db["workforce_vehicle"].find_one({
#                     "vehicle_id": str(oid)
#                 })             
#                 if wf_vehicle:
#                     wf_oid = safe_objectid(wf_vehicle.get("workforce_id"))
#                     if wf_oid:
#                         workforce = db["workforce"].find_one({"_id": wf_oid})                        
#                         if workforce:
#                             mapped_data["workforce_info"] = {
#                                 "workforce_id": str(workforce["_id"]),
#                                 "first_name": workforce.get("first_name"),
#                                 "last_name": workforce.get("last_name"),
#                                 "mobile_number": workforce.get("mobile_number"),
#                                 "email_id": workforce.get("email_id"),
#                                 "status": workforce.get("status"),
#                                 "photo": workforce.get("photo"),
#                                 "fleet_info": workforce.get("fleet_info"),
#                             }
#         elif associated_entity_type == "workforce" and oid:
#             workforce = db["workforce"].find_one({"_id": oid})
#             if workforce:
#                 mapped_data = {
#                     "type": "workforce",
#                     "workforce_id": str(workforce["_id"]),
#                     "first_name": workforce.get("first_name"),
#                     "last_name": workforce.get("last_name"),
#                     "mobile_number": workforce.get("mobile_number"),
#                     "email_id": workforce.get("email_id"),
#                     "status": workforce.get("status"),
#                     "photo": workforce.get("photo"),
#                     "fleet_info": workforce.get("fleet_info"),
#                 }

#         # Always include mapped_details, even if None
#         d["mapped_details"] = mapped_data
#         devices.append(d)

#     total = db["iot_devices"].count_documents(filter_query)
#     return {"total_count": total, "devices": devices}

def list_iot_devices_with_mapping_service(
    skip: int,
    limit: int,
    q: Optional[str],
    device_type: Optional[str],
    status: Optional[str],
    date_from: Optional[str],
    date_to: Optional[str],
    associated_entity_type: Optional[str],
    db
):
    filter_query = {}

    if q:
        if q.startswith("#"):
            tag_val = q[1:]
            regex_query = {"$regex": tag_val, "$options": "i"}
            filter_query["tag_name"] = regex_query
        else:
            regex_query = {"$regex": q, "$options": "i"}
            filter_query["$or"] = [
                {"model_name": regex_query},
                {"fleet_name": regex_query},
                {"last_name": regex_query},
                {"mobile_number": regex_query},
                {"email_id": regex_query},
                {"registration_number": regex_query},
                {"vin_number": regex_query},
                {"installation_details": regex_query}
            ]

    if device_type:
        filter_query["device_type"] = device_type

    if status:
        filter_query["status"] = status

    if associated_entity_type:
        filter_query["associated_entity_type"] = associated_entity_type

    if date_from or date_to:
        date_filter = {}
        if date_from:
            date_filter["$gte"] = datetime.strptime(date_from, "%Y-%m-%d")
        if date_to:
            date_filter["$lte"] = datetime.strptime(date_to, "%Y-%m-%d")
        filter_query["created_date"] = date_filter

    cursor = db["iot_devices"].find(filter_query).skip(skip).limit(limit)
    devices = []

    for d in cursor:
        d["id"] = str(d["_id"])
        associated_type = d.get("associated_entity_type")
        associated_id = d.get("associated_entity_id")
        mapped_data = None
        oid = safe_objectid(associated_id)

        # ============= VEHICLE MAPPING =============
        if associated_type == "vehicle" and oid:
            fleet = db["fleets"].find_one({"_id": oid})
            if fleet:
                mapped_data = {
                    "type": "fleet",
                    "fleet_id": str(fleet["_id"]),
                    "fleet_name": fleet.get("fleet_name"),
                    "registration_number": fleet.get("registration_number"),
                    "vehicle_type": fleet.get("vehicle_type"),
                    "status": fleet.get("status"),
                    "photo": fleet.get("vehicle_image"),
                }

                # 🔍 Check if this vehicle has a mapped workforce
                wf_vehicle = db["workforce_vehicle"].find_one({"vehicle_id": str(oid)})
                if wf_vehicle:
                    wf_oid = safe_objectid(wf_vehicle.get("workforce_id"))
                    if wf_oid:
                        workforce = db["workforce"].find_one({"_id": wf_oid})
                        if workforce:
                            mapped_data["workforce_info"] = {
                                "workforce_id": str(workforce["_id"]),
                                "first_name": workforce.get("first_name"),
                                "last_name": workforce.get("last_name"),
                                "mobile_number": workforce.get("mobile_number"),
                                "email_id": workforce.get("email_id"),
                                "status": workforce.get("status"),
                                "photo": workforce.get("photo"),
                                "fleet_info": workforce.get("fleet_info"),
                            }

                # 🛰️ Get all IoT devices mapped to this same vehicle                
                related_devices = list(db["iot_devices"].find({
                    "associated_entity_type": "vehicle",
                    "associated_entity_id": str(fleet["_id"])
                }))                
                for rd in related_devices:
                    rd["id"] = str(rd["_id"])
                    del rd["_id"]  # ✅ remove the ObjectId field
                mapped_data["device_list"] = related_devices  # ✅ added device list here                
        # ============= WORKFORCE MAPPING =============
        elif associated_type == "workforce" and oid:
            workforce = db["workforce"].find_one({"_id": oid})
            if workforce:
                mapped_data = {
                    "type": "workforce",
                    "workforce_id": str(workforce["_id"]),
                    "first_name": workforce.get("first_name"),
                    "last_name": workforce.get("last_name"),
                    "mobile_number": workforce.get("mobile_number"),
                    "email_id": workforce.get("email_id"),
                    "status": workforce.get("status"),
                    "photo": workforce.get("photo"),
                    "fleet_info": workforce.get("fleet_info"),
                }

                # 🛰️ Get all IoT devices mapped to this same workforce
                related_devices = list(db["iot_devices"].find({
                    "associated_entity_type": "workforce",
                    "associated_entity_id": str(workforce["_id"])
                }))                
                for rd in related_devices:
                    rd["id"] = str(rd["_id"])
                    del rd["_id"]  # ✅ remove the ObjectId field
                mapped_data["device_list"] = related_devices  # ✅ added device list here

                print("related_devicesrelated_devicesrelated_devicesrelated_devices")
                print(mapped_data)
                print("************************************************************")

        # Always include mapped_details
        d["mapped_details"] = mapped_data
        devices.append(d)

    total = db["iot_devices"].count_documents(filter_query)
    return {"total_count": total, "devices": devices}

# services/iotdevices.py
def list_nearby_devices_service(
    latitude: float,
    longitude: float,
    radius_km: float,
    db
):
    base_geo_query = {
        "$geoNear": {
            "near": {"type": "Point", "coordinates": [longitude, latitude]},
            "distanceField": "distance_meters",
            "spherical": True,
            "maxDistance": radius_km * 1000,
            "query": {"status": "active"},
        }
    }

    pipeline = [base_geo_query]
    cursor = db["iot_devices"].aggregate(pipeline)

    devices = []
    for d in cursor:
        device_info = {
            "id": str(d["_id"]),
            "account_id": str(d.get("account_id", "")),   # ✅ required
            "user_id": str(d.get("user_id", "")) if d.get("user_id") else None,
            "associated_entity_type": d.get("associated_entity_type"),  # ✅ required
            "associated_entity_id": str(d.get("associated_entity_id", "")),  # ✅ required
            "device_type": d.get("device_type"),  # ✅ required
            "installation_details": d.get("installation_details", ""),  # ✅ required
            "status": d.get("status"),
            "last_updated": d.get("last_updated"),
            "model_name": d.get("model_name"),
            "uuid": d.get("uuid"),

            # Extra fields
            "distance_km": round(d.get("distance_meters", 0) / 1000, 2),
            "last_location": d.get("last_location"),
        }

        # 🔗 mapped entity
        mapped_data = None
        oid = safe_objectid(d.get("associated_entity_id"))
        associated_type = d.get("associated_entity_type")

        if associated_type == "vehicle" and oid:
            fleet = db["fleets"].find_one({"_id": oid})
            if fleet:
                mapped_data = {
                    "type": "fleet",
                    "fleet_id": str(fleet["_id"]),
                    "fleet_name": fleet.get("fleet_name"),
                    "registration_number": fleet.get("registration_number"),
                    "vehicle_type": fleet.get("vehicle_type"),
                    "status": fleet.get("status"),
                }

        elif associated_type == "workforce" and oid:
            workforce = db["workforce"].find_one({"_id": oid})
            if workforce:
                mapped_data = {
                    "type": "workforce",
                    "workforce_id": str(workforce["_id"]),
                    "first_name": workforce.get("first_name"),
                    "last_name": workforce.get("last_name"),
                    "mobile_number": workforce.get("mobile_number"),
                    "email_id": workforce.get("email_id"),
                    "status": workforce.get("status"),
                }

        device_info["mapped_details"] = mapped_data
        devices.append(device_info)

    # --- count ---
    count_pipeline = [base_geo_query, {"$count": "total"}]
    count_result = list(db["iot_devices"].aggregate(count_pipeline))
    total = count_result[0]["total"] if count_result else 0

    return {"total_count": total, "devices": devices}

def list_nearby_devices_service_socket(
    latitude: float,
    longitude: float,
    radius_km: float,
    db
):
    base_geo_query = {
        "$geoNear": {
            "near": {"type": "Point", "coordinates": [longitude, latitude]},
            "distanceField": "distance_meters",
            "spherical": True,
            "maxDistance": 1000 * 1000,
            "query": {"status": "active"},
        }
    }

    pipeline = [base_geo_query]
    cursor = db["iot_devices"].aggregate(pipeline)

    devices = []
    for d in cursor:
        device_info = {
            "id": str(d["_id"]),
            "account_id": str(d.get("account_id", "")),
            "user_id": str(d.get("user_id", "")) if d.get("user_id") else None,
            "associated_entity_type": d.get("associated_entity_type"),
            "associated_entity_id": str(d.get("associated_entity_id", "")),
            "device_type": d.get("device_type"),
            "installation_details": d.get("installation_details", ""),
            "status": d.get("status"),
            "last_updated": d.get("last_updated"),
            "model_name": d.get("model_name"),
            "uuid": d.get("uuid"),
            "distance_km": round(d.get("distance_meters", 0) / 1000, 2),
            "last_location": d.get("last_location"),
        }

        mapped_data = None
        oid = safe_objectid(d.get("associated_entity_id"))
        associated_type = d.get("associated_entity_type")

        if associated_type == "vehicle" and oid:
            fleet = db["fleets"].find_one({"_id": oid})
            if fleet:
                mapped_data = {
                    "type": "fleet",
                    "fleet_id": str(fleet["_id"]),
                    "fleet_name": fleet.get("fleet_name"),
                    "registration_number": fleet.get("registration_number"),
                    "vehicle_type": fleet.get("vehicle_type"),
                    "status": fleet.get("status"),
                }

        elif associated_type == "workforce" and oid:
            workforce = db["workforce"].find_one({"_id": oid})
            if workforce:
                mapped_data = {
                    "type": "workforce",
                    "workforce_id": str(workforce["_id"]),
                    "first_name": workforce.get("first_name"),
                    "last_name": workforce.get("last_name"),
                    "mobile_number": workforce.get("mobile_number"),
                    "email_id": workforce.get("email_id"),
                    "status": workforce.get("status"),
                }

        device_info["mapped_details"] = mapped_data
        devices.append(device_info)

    count_pipeline = [base_geo_query, {"$count": "total"}]
    count_result = list(db["iot_devices"].aggregate(count_pipeline))
    total = count_result[0]["total"] if count_result else 0

    return {"total_count": total, "devices": devices}
