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 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)
    return {"total_count": total, "users": 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],
    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
        oid = safe_objectid(associated_id)   # ✅ never raises

        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"),
                }

        # 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}

# 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}