"""
4Alarm - Core alarmas routes (multi-tenant)
All queries are scoped to the user's tenant_id.
"""
from fastapi import APIRouter, HTTPException, Depends, Query, UploadFile, File
from fastapi.responses import Response, HTMLResponse
from datetime import datetime, timezone, timedelta
import uuid
import os
import json
import base64


def setup_alarmas_routes(get_current_user, db):
    router = APIRouter(prefix="/alarmas", tags=["alarmas"])

    # ── Helpers ────────────────────────────────────────────────

    async def _get_tenant(user):
        """Retrieve the tenant config for the current user."""
        tid = user.get("tenant_id", "")
        if not tid or tid == "__global__":
            raise HTTPException(status_code=400, detail="Usuario sin tenant asignado")
        tenant = await db.tenants.find_one({"id": tid}, {"_id": 0})
        if not tenant:
            raise HTTPException(status_code=404, detail="Tenant no encontrado")
        return tenant

    def _require_admin(user):
        if user.get("rol") not in ("admin", "superadmin", "gerente"):
            raise HTTPException(status_code=403, detail="Solo admin")

    async def _send_push_to_user(tenant, usuario_id, titulo, mensaje, lat=None, lng=None):
        """Send a web push notification to a specific user."""
        try:
            from pywebpush import webpush
            sub_doc = await db.push_subscriptions.find_one({"usuario_id": usuario_id, "tenant_id": tenant["id"]})
            if not sub_doc or not sub_doc.get("subscription"):
                return
            vapid_private = tenant.get("vapid_private_key") or os.environ.get("VAPID_PRIVATE_KEY")
            if not vapid_private:
                return
            icon_url = f"/api/alarmas/icon/{tenant['id']}/192"
            payload = {
                "title": titulo,
                "body": mensaje,
                "icon": icon_url,
                "badge": icon_url,
                "tag": f"alarma-{uuid.uuid4().hex[:8]}",
                "requireInteraction": True,
                "vibrate": [300, 100, 300, 100, 300],
                "data": {"icon": icon_url},
            }
            if lat and lng:
                payload["data"]["lat"] = lat
                payload["data"]["lng"] = lng
                payload["data"]["url"] = f"https://www.google.com/maps?q={lat},{lng}"
            webpush(
                subscription_info=sub_doc["subscription"],
                data=json.dumps(payload),
                vapid_private_key=vapid_private,
                vapid_claims={"sub": f"mailto:alertas@4alarm.cl"},
                headers={"Urgency": "high", "TTL": "86400"},
            )
        except Exception as e:
            print(f"[4Alarm] Push error for {usuario_id}: {e}")

    async def _crear_sala_chat(tenant_id, evento_tipo, evento_usuario_id, evento_usuario_nombre, participante_ids, lat=None, lng=None):
        config = await db.chat_config.find_one({"tenant_id": tenant_id})
        if not config or not config.get("activo"):
            return None
        duracion_min = config.get("duracion_minutos", 15)
        sala = {
            "id": str(uuid.uuid4()),
            "tenant_id": tenant_id,
            "evento_tipo": evento_tipo,
            "evento_usuario_id": evento_usuario_id,
            "evento_usuario_nombre": evento_usuario_nombre,
            "participantes": list(set(participante_ids)),
            "lat": lat, "lng": lng,
            "duracion_minutos": duracion_min,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "expires_at": (datetime.now(timezone.utc) + timedelta(minutes=duracion_min)).isoformat(),
            "activa": True,
        }
        await db.chat_rooms.insert_one(sala)
        sala.pop("_id", None)
        return sala

    # ── Tuya direct ────────────────────────────────────────────

    @router.get("/tuya/dispositivos")
    async def listar_dispositivos_tuya(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        from services.tuya import tuya_request
        try:
            resp = tuya_request(tenant, "get", "/v2.0/cloud/thing/device")
            if resp.get("success"):
                devices = resp.get("result", [])
                result = [{
                    "tuya_device_id": d["id"],
                    "name": d.get("customName") or d.get("name"),
                    "product_name": d.get("productName"),
                    "is_online": d.get("isOnline", False),
                    "icon": d.get("icon"),
                    "category": d.get("category"),
                } for d in devices]
                await db.tuya_cache.delete_many({"tenant_id": tid})
                if result:
                    await db.tuya_cache.insert_many([{**d, "tenant_id": tid, "_cached_at": datetime.now(timezone.utc).isoformat()} for d in result])
                return result
        except Exception as e:
            print(f"[4Alarm] Tuya error: {e}")
        cached = await db.tuya_cache.find({"tenant_id": tid}, {"_id": 0, "_cached_at": 0, "tenant_id": 0}).to_list(100)
        if cached:
            return cached
        registrados = await db.devices.find({"tenant_id": tid, "activo": True}, {"_id": 0}).to_list(100)
        return [{
            "tuya_device_id": d.get("tuya_device_id"),
            "name": d.get("nombre"),
            "product_name": "Registrado",
            "is_online": False,
            "icon": None,
            "category": d.get("categoria", "tdq"),
        } for d in registrados]

    # ── CRUD Devices ───────────────────────────────────────────

    @router.get("/dispositivos")
    async def listar_dispositivos(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        return await db.devices.find({"tenant_id": tenant["id"], "activo": True}, {"_id": 0}).sort("nombre", 1).to_list(200)

    @router.post("/dispositivos")
    async def registrar_dispositivo(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        doc = {
            "id": str(uuid.uuid4()),
            "tenant_id": tenant["id"],
            "tuya_device_id": data["tuya_device_id"],
            "nombre": data["nombre"],
            "codigo_switch": data.get("codigo_switch", "switch_1"),
            "icono": data.get("icono", "power"),
            "activo": True,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.devices.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @router.put("/dispositivos/{disp_id}")
    async def actualizar_dispositivo(disp_id: str, data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        update = {k: v for k, v in data.items() if k in ("nombre", "codigo_switch", "icono", "activo")}
        result = await db.devices.update_one({"id": disp_id, "tenant_id": tenant["id"]}, {"$set": update})
        if result.matched_count == 0:
            raise HTTPException(status_code=404, detail="No encontrado")
        return await db.devices.find_one({"id": disp_id}, {"_id": 0})

    @router.delete("/dispositivos/{disp_id}")
    async def eliminar_dispositivo(disp_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        await db.devices.delete_one({"id": disp_id, "tenant_id": tenant["id"]})
        await db.assignments.delete_many({"dispositivo_id": disp_id, "tenant_id": tenant["id"]})
        return {"message": "Eliminado"}

    # ── Users (tenant admin) ──────────────────────────────────

    @router.post("/usuarios")
    async def crear_usuario(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        import bcrypt
        email = (data.get("email") or "").strip().lower()
        nombre = (data.get("nombre") or "").strip()
        password = data.get("password", "")
        if not email or not nombre or not password:
            raise HTTPException(status_code=400, detail="Nombre, email y contraseña requeridos")
        if len(password) < 6:
            raise HTTPException(status_code=400, detail="Contraseña mínimo 6 caracteres")
        existing = await db.users.find_one({"email": email})
        if existing:
            raise HTTPException(status_code=400, detail="Email ya registrado")
        pw_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
        nuevo = {
            "id": str(uuid.uuid4()),
            "email": email,
            "nombre": nombre,
            "direccion": (data.get("direccion") or "").strip(),
            "rol": "usuario",
            "tenant_id": tenant["id"],
            "password_hash": pw_hash,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.users.insert_one(nuevo)
        nuevo.pop("_id", None)
        nuevo.pop("password_hash", None)
        return nuevo

    @router.get("/usuarios")
    async def listar_usuarios(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        usuarios = await db.users.find(
            {"tenant_id": tenant["id"]}, {"_id": 0, "password_hash": 0}
        ).sort("nombre", 1).to_list(500)
        return usuarios

    # ── Assignments ────────────────────────────────────────────

    @router.get("/asignaciones")
    async def listar_asignaciones(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        return await db.assignments.find({"tenant_id": tenant["id"]}, {"_id": 0}).to_list(500)

    @router.post("/asignaciones")
    async def asignar_dispositivo(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        existing = await db.assignments.find_one({
            "tenant_id": tenant["id"], "usuario_id": data["usuario_id"], "dispositivo_id": data["dispositivo_id"]
        })
        if existing:
            raise HTTPException(status_code=400, detail="Ya asignado")
        doc = {
            "id": str(uuid.uuid4()),
            "tenant_id": tenant["id"],
            "usuario_id": data["usuario_id"],
            "dispositivo_id": data["dispositivo_id"],
            "notificaciones_push": data.get("notificaciones_push", True),
            "notificaciones_inapp": data.get("notificaciones_inapp", True),
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.assignments.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @router.delete("/asignaciones/{asig_id}")
    async def desasignar(asig_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        await db.assignments.delete_one({"id": asig_id, "tenant_id": tenant["id"]})
        return {"message": "Desasignado"}

    # ── My devices (user view) ─────────────────────────────────

    @router.get("/mis-dispositivos")
    async def mis_dispositivos(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        user_id = user["id"]
        is_admin = user.get("rol") in ("admin", "superadmin", "gerente")

        if is_admin:
            dispositivos = await db.devices.find({"tenant_id": tid, "activo": True}, {"_id": 0}).to_list(200)
        else:
            asigs = await db.assignments.find({"tenant_id": tid, "usuario_id": user_id}, {"_id": 0}).to_list(50)
            disp_ids = [a["dispositivo_id"] for a in asigs]
            if not disp_ids:
                return []
            dispositivos = await db.devices.find({"id": {"$in": disp_ids}, "tenant_id": tid, "activo": True}, {"_id": 0}).to_list(50)

        from services.tuya import tuya_request
        for d in dispositivos:
            estado = await db.device_states.find_one({"dispositivo_id": d["id"], "tenant_id": tid}, {"_id": 0})
            if estado:
                d["switch"] = estado.get("switch")
                d["online"] = estado.get("online", True)
            else:
                try:
                    resp = tuya_request(tenant, "get", f"/v1.0/iot-03/devices/{d['tuya_device_id']}/status")
                    switch_value = None
                    if resp.get("success"):
                        for s in resp.get("result", []):
                            if s.get("code") == d.get("codigo_switch", "switch_1"):
                                switch_value = s.get("value")
                                break
                    d["switch"] = switch_value
                    d["online"] = True
                    await db.device_states.update_one(
                        {"dispositivo_id": d["id"], "tenant_id": tid},
                        {"$set": {"dispositivo_id": d["id"], "tenant_id": tid, "switch": switch_value, "online": True, "updated_at": datetime.now(timezone.utc).isoformat()}},
                        upsert=True,
                    )
                except Exception:
                    d["switch"] = None
                    d["online"] = False
        return dispositivos

    # ── Toggle device ──────────────────────────────────────────

    @router.post("/mis-dispositivos/{disp_id}/toggle")
    async def toggle_dispositivo(disp_id: str, data: dict, user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        user_id = user["id"]
        is_admin = user.get("rol") in ("admin", "superadmin", "gerente")

        if not is_admin:
            asig = await db.assignments.find_one({"tenant_id": tid, "usuario_id": user_id, "dispositivo_id": disp_id})
            if not asig:
                raise HTTPException(status_code=403, detail="Sin acceso")

        dispositivo = await db.devices.find_one({"id": disp_id, "tenant_id": tid}, {"_id": 0})
        if not dispositivo:
            raise HTTPException(status_code=404, detail="No encontrado")

        action = data.get("action")
        if action not in ("on", "off"):
            raise HTTPException(status_code=400, detail="action: 'on' o 'off'")

        code = dispositivo.get("codigo_switch", "switch_1")
        value = action == "on"
        from services.tuya import tuya_request
        commands = {"commands": [{"code": code, "value": value}]}
        resp = tuya_request(tenant, "post", f"/v1.0/iot-03/devices/{dispositivo['tuya_device_id']}/commands", commands)
        if not resp.get("success"):
            raise HTTPException(status_code=502, detail=resp.get("msg", "Error Tuya"))

        await db.device_states.update_one(
            {"dispositivo_id": disp_id, "tenant_id": tid},
            {"$set": {"dispositivo_id": disp_id, "tenant_id": tid, "switch": value, "online": True, "updated_at": datetime.now(timezone.utc).isoformat()}},
            upsert=True,
        )

        evento = {
            "id": str(uuid.uuid4()),
            "tenant_id": tid,
            "dispositivo_id": disp_id,
            "dispositivo_nombre": dispositivo.get("nombre"),
            "usuario_id": user_id,
            "usuario_nombre": user.get("nombre", ""),
            "action": action,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.events.insert_one(evento)

        otros = await db.assignments.find(
            {"tenant_id": tid, "dispositivo_id": disp_id, "usuario_id": {"$ne": user_id}}, {"_id": 0}
        ).to_list(100)

        accion_texto = "ENCENDIDO" if action == "on" else "APAGADO"
        user_dir = (user.get("direccion") or "").strip()
        quien = user.get("nombre", "Usuario")
        if user_dir:
            quien = f"{quien} de {user_dir}"
        titulo = f"Alerta {tenant.get('nombre', '')} - {dispositivo.get('nombre')} {accion_texto}"
        mensaje = f"{quien} ha {accion_texto.lower()} {dispositivo.get('nombre')}"
        lat, lng = data.get("lat"), data.get("lng")

        for otro in otros:
            notif = {
                "id": str(uuid.uuid4()),
                "tenant_id": tid,
                "usuario_id": otro["usuario_id"],
                "titulo": titulo,
                "mensaje": mensaje,
                "dispositivo_id": disp_id,
                "lat": lat, "lng": lng,
                "tipo": "toggle",
                "leida": False,
                "created_at": datetime.now(timezone.utc).isoformat(),
            }
            await db.notifications.insert_one(notif)
            await _send_push_to_user(tenant, otro["usuario_id"], titulo, mensaje, lat, lng)

        todos = [user_id] + [o["usuario_id"] for o in otros]
        await _crear_sala_chat(tid, "toggle", user_id, user.get("nombre", ""), todos, lat, lng)
        return {"success": True, "action": action, "device_name": dispositivo.get("nombre"), "message": mensaje}

    # ── SOS ────────────────────────────────────────────────────

    @router.post("/sos")
    async def enviar_sos(data: dict, user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        user_id = user["id"]
        lat, lng = data.get("lat"), data.get("lng")

        titulo = f"SOS {tenant.get('nombre', 'Alarma')}"
        user_dir = (user.get("direccion") or "").strip()
        quien = user.get("nombre", "Usuario")
        if user_dir:
            quien = f"{quien} de {user_dir}"
        mensaje = f"Vecino {quien} necesita ayuda"

        mis_asig = await db.assignments.find({"tenant_id": tid, "usuario_id": user_id}, {"_id": 0}).to_list(50)
        mis_disp_ids = [a["dispositivo_id"] for a in mis_asig]
        if user.get("rol") in ("admin", "superadmin", "gerente"):
            todos_d = await db.devices.find({"tenant_id": tid, "activo": True}, {"_id": 0, "id": 1}).to_list(200)
            mis_disp_ids = [d["id"] for d in todos_d]

        otras_asig = []
        if mis_disp_ids:
            otras_asig = await db.assignments.find(
                {"tenant_id": tid, "dispositivo_id": {"$in": mis_disp_ids}, "usuario_id": {"$ne": user_id}}, {"_id": 0}
            ).to_list(200)
        usuarios_notificar = list(set(a["usuario_id"] for a in otras_asig))

        await db.events.insert_one({
            "id": str(uuid.uuid4()), "tenant_id": tid,
            "dispositivo_id": None, "dispositivo_nombre": "SOS",
            "usuario_id": user_id, "usuario_nombre": user.get("nombre", ""),
            "action": "sos", "lat": lat, "lng": lng,
            "created_at": datetime.now(timezone.utc).isoformat(),
        })

        for uid in usuarios_notificar:
            notif = {
                "id": str(uuid.uuid4()), "tenant_id": tid,
                "usuario_id": uid, "titulo": titulo, "mensaje": mensaje,
                "lat": lat, "lng": lng, "tipo": "sos", "leida": False,
                "created_at": datetime.now(timezone.utc).isoformat(),
            }
            await db.notifications.insert_one(notif)
            await _send_push_to_user(tenant, uid, titulo, mensaje, lat, lng)

        todos = [user_id] + usuarios_notificar
        await _crear_sala_chat(tid, "sos", user_id, user.get("nombre", ""), todos, lat, lng)
        return {"success": True, "message": mensaje, "notificados": len(usuarios_notificar)}

    # ── Push ───────────────────────────────────────────────────

    @router.get("/vapid-public-key")
    async def get_vapid_key(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        key = tenant.get("vapid_public_key") or os.environ.get("VAPID_PUBLIC_KEY", "")
        return {"key": key}

    @router.post("/push/subscribe")
    async def push_subscribe(data: dict, user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        subscription = data.get("subscription")
        if not subscription:
            raise HTTPException(status_code=400, detail="Subscription requerida")
        await db.push_subscriptions.update_one(
            {"usuario_id": user["id"], "tenant_id": tenant["id"]},
            {"$set": {"usuario_id": user["id"], "tenant_id": tenant["id"], "subscription": subscription, "updated_at": datetime.now(timezone.utc).isoformat()}},
            upsert=True,
        )
        return {"message": "Suscrito"}

    @router.post("/push/unsubscribe")
    async def push_unsubscribe(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        await db.push_subscriptions.delete_one({"usuario_id": user["id"], "tenant_id": tenant["id"]})
        return {"message": "Desuscrito"}

    # ── Notifications / Preferences / Historial ────────────────

    @router.get("/notificaciones")
    async def mis_notificaciones(user: dict = Depends(get_current_user)):
        return await db.notifications.find(
            {"usuario_id": user["id"], "leida": False}, {"_id": 0}
        ).sort("created_at", -1).limit(50).to_list(50)

    @router.post("/notificaciones/marcar-leidas")
    async def marcar_leidas(user: dict = Depends(get_current_user)):
        await db.notifications.update_many({"usuario_id": user["id"], "leida": False}, {"$set": {"leida": True}})
        return {"message": "OK"}

    @router.get("/preferencias")
    async def get_preferencias(user: dict = Depends(get_current_user)):
        prefs = await db.preferences.find_one({"usuario_id": user["id"]}, {"_id": 0})
        return prefs or {"usuario_id": user["id"], "sonido": "clasico"}

    @router.put("/preferencias")
    async def update_preferencias(data: dict, user: dict = Depends(get_current_user)):
        await db.preferences.update_one(
            {"usuario_id": user["id"]},
            {"$set": {"usuario_id": user["id"], "sonido": data.get("sonido", "clasico"), "updated_at": datetime.now(timezone.utc).isoformat()}},
            upsert=True,
        )
        return {"message": "Preferencias guardadas"}

    @router.put("/admin/preferencias/{usuario_id}")
    async def admin_set_preferencias(usuario_id: str, data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        await db.preferences.update_one(
            {"usuario_id": usuario_id},
            {"$set": {"usuario_id": usuario_id, "sonido": data.get("sonido", "clasico"), "updated_at": datetime.now(timezone.utc).isoformat()}},
            upsert=True,
        )
        return {"message": "Preferencias actualizadas"}

    @router.get("/historial")
    async def historial(limit: int = Query(50), user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        return await db.events.find({"tenant_id": tenant["id"]}, {"_id": 0}).sort("created_at", -1).limit(limit).to_list(limit)

    # ── Test alert ─────────────────────────────────────────────

    @router.post("/test-alerta")
    async def test_alerta(data: dict, user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        user_id = user["id"]
        is_admin = user.get("rol") in ("admin", "superadmin", "gerente")
        if not is_admin:
            prefs = await db.preferences.find_one({"usuario_id": user_id})
            if not prefs or not prefs.get("puede_probar"):
                raise HTTPException(status_code=403, detail="No tiene permiso para pruebas")

        lat, lng = data.get("lat"), data.get("lng")
        titulo = f"PRUEBA {tenant.get('nombre', 'Alarma')}"
        user_dir = (user.get("direccion") or "").strip()
        quien = user.get("nombre", "Usuario")
        if user_dir:
            quien = f"{quien} de {user_dir}"
        mensaje = f"Prueba de alerta de {quien}"

        mis_asig = await db.assignments.find({"tenant_id": tid, "usuario_id": user_id}, {"_id": 0}).to_list(50)
        mis_disp_ids = [a["dispositivo_id"] for a in mis_asig]
        if is_admin:
            todos_d = await db.devices.find({"tenant_id": tid, "activo": True}, {"_id": 0, "id": 1}).to_list(200)
            mis_disp_ids = [d["id"] for d in todos_d]
        otras_asig = []
        if mis_disp_ids:
            otras_asig = await db.assignments.find(
                {"tenant_id": tid, "dispositivo_id": {"$in": mis_disp_ids}, "usuario_id": {"$ne": user_id}}, {"_id": 0}
            ).to_list(200)
        usuarios_notificar = list(set(a["usuario_id"] for a in otras_asig))

        await db.events.insert_one({
            "id": str(uuid.uuid4()), "tenant_id": tid,
            "dispositivo_id": None, "dispositivo_nombre": "TEST",
            "usuario_id": user_id, "usuario_nombre": user.get("nombre", ""),
            "action": "test", "lat": lat, "lng": lng,
            "created_at": datetime.now(timezone.utc).isoformat(),
        })

        for uid in [user_id] + usuarios_notificar:
            notif = {
                "id": str(uuid.uuid4()), "tenant_id": tid,
                "usuario_id": uid, "titulo": titulo, "mensaje": mensaje,
                "lat": lat, "lng": lng, "tipo": "test", "leida": False,
                "created_at": datetime.now(timezone.utc).isoformat(),
            }
            await db.notifications.insert_one(notif)
            await _send_push_to_user(tenant, uid, titulo, mensaje, lat, lng)
        return {"success": True, "message": f"Prueba enviada a {len(usuarios_notificar) + 1} usuario(s)"}

    @router.put("/admin/toggle-prueba/{usuario_id}")
    async def toggle_prueba(usuario_id: str, data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        puede = data.get("puede_probar", False)
        await db.preferences.update_one(
            {"usuario_id": usuario_id},
            {"$set": {"usuario_id": usuario_id, "puede_probar": puede}},
            upsert=True,
        )
        return {"message": f"Pruebas {'habilitadas' if puede else 'deshabilitadas'}"}

    # ── Diagnostics ────────────────────────────────────────────

    @router.get("/diagnostico")
    async def diagnostico_tuya(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        resultado = {
            "tuya_configured": bool(tenant.get("tuya_access_id")),
            "tuya_endpoint": tenant.get("tuya_endpoint", "no configurado"),
            "connection": "error",
            "devices_count": 0,
            "error": None,
        }
        try:
            from services.tuya import tuya_request
            resp = tuya_request(tenant, "get", "/v2.0/cloud/thing/device")
            if resp.get("success"):
                resultado["connection"] = "ok"
                resultado["devices_count"] = len(resp.get("result", []))
            else:
                resultado["error"] = f"{resp.get('msg')} (code: {resp.get('code')})"
        except Exception as e:
            resultado["error"] = str(e)
        return resultado

    @router.post("/diagnostico-push")
    async def diagnostico_push(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        sub_doc = await db.push_subscriptions.find_one({"usuario_id": user["id"], "tenant_id": tenant["id"]})
        if not sub_doc:
            raise HTTPException(status_code=400, detail="No has activado notificaciones push")
        await _send_push_to_user(tenant, user["id"], "Prueba de diagnóstico",
                                 f"Si ves esta notificación, está configurado correctamente, {user.get('nombre', '')}.")
        return {"message": "Enviada. Revisa tu teléfono."}

    @router.post("/diagnostico-resultado")
    async def guardar_diagnostico_resultado(data: dict, user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        respuestas = {
            "visto": bool(data.get("visto")),
            "sonido": bool(data.get("sonido")),
            "vibro": bool(data.get("vibro")),
            "bloqueo": bool(data.get("bloqueo")),
        }
        ok_count = sum(1 for v in respuestas.values() if v)
        estado = "ok" if ok_count == 4 else "parcial" if ok_count >= 1 else "falla"
        doc = {
            "id": str(uuid.uuid4()), "tenant_id": tenant["id"],
            "usuario_id": user["id"], "usuario_nombre": user.get("nombre", ""),
            "usuario_direccion": (user.get("direccion") or ""),
            "respuestas": respuestas, "estado": estado, "ok_count": ok_count,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.diagnostics.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @router.get("/admin/diagnosticos")
    async def listar_diagnosticos_admin(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        tid = tenant["id"]
        asigs = await db.assignments.find({"tenant_id": tid}, {"_id": 0, "usuario_id": 1}).to_list(500)
        user_ids = list(set(a["usuario_id"] for a in asigs))
        usuarios = await db.users.find({"id": {"$in": user_ids}}, {"_id": 0, "id": 1, "nombre": 1, "email": 1, "direccion": 1}).to_list(500)
        pipeline = [
            {"$match": {"tenant_id": tid, "usuario_id": {"$in": user_ids}}},
            {"$sort": {"created_at": -1}},
            {"$group": {"_id": "$usuario_id", "doc": {"$first": "$$ROOT"}}},
            {"$replaceRoot": {"newRoot": "$doc"}},
        ]
        ultimos = await db.diagnostics.aggregate(pipeline).to_list(500)
        map_diag = {}
        for d in ultimos:
            d.pop("_id", None)
            map_diag[d["usuario_id"]] = d
        resultado = []
        for u in usuarios:
            diag = map_diag.get(u["id"])
            resultado.append({
                "usuario_id": u["id"], "nombre": u.get("nombre", ""),
                "email": u.get("email", ""), "direccion": u.get("direccion", ""),
                "ultimo_diagnostico": diag, "estado": diag["estado"] if diag else "sin_diagnostico",
            })
        orden = {"falla": 0, "parcial": 1, "sin_diagnostico": 2, "ok": 3}
        resultado.sort(key=lambda r: (orden.get(r["estado"], 4), r["nombre"]))
        return resultado

    # ── Chat ───────────────────────────────────────────────────

    @router.get("/chat/config")
    async def get_chat_config(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        config = await db.chat_config.find_one({"tenant_id": tenant["id"]}, {"_id": 0})
        return config or {"activo": False, "duracion_minutos": 15}

    @router.put("/chat/config")
    async def set_chat_config(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        activo = bool(data.get("activo", False))
        await db.chat_config.update_one(
            {"tenant_id": tenant["id"]},
            {"$set": {"tenant_id": tenant["id"], "activo": activo, "duracion_minutos": data.get("duracion_minutos", 15)}},
            upsert=True,
        )
        if not activo:
            await db.chat_rooms.update_many(
                {"tenant_id": tenant["id"], "activa": True},
                {"$set": {"activa": False, "cerrada_por": "config_global", "cerrada_at": datetime.now(timezone.utc).isoformat()}},
            )
        return {"message": "Configuración guardada"}

    @router.get("/chat/active")
    async def get_active_chat(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        cfg = await db.chat_config.find_one({"tenant_id": tenant["id"]})
        if not cfg or not cfg.get("activo"):
            return None
        now = datetime.now(timezone.utc).isoformat()
        sala = await db.chat_rooms.find_one(
            {"tenant_id": tenant["id"], "participantes": user["id"], "activa": True, "expires_at": {"$gt": now}}, {"_id": 0}
        )
        if not sala:
            await db.chat_rooms.update_many(
                {"tenant_id": tenant["id"], "activa": True, "expires_at": {"$lt": now}},
                {"$set": {"activa": False}},
            )
            return None
        return sala

    @router.get("/chat/rooms/{room_id}/messages")
    async def get_chat_messages(room_id: str, after: str = Query(None), user: dict = Depends(get_current_user)):
        sala = await db.chat_rooms.find_one({"id": room_id, "participantes": user["id"]})
        if not sala:
            raise HTTPException(status_code=403, detail="Sin acceso")
        query = {"room_id": room_id}
        if after:
            query["created_at"] = {"$gt": after}
        return await db.chat_messages.find(query, {"_id": 0}).sort("created_at", 1).limit(200).to_list(200)

    @router.post("/chat/rooms/{room_id}/messages")
    async def send_chat_message(room_id: str, data: dict, user: dict = Depends(get_current_user)):
        sala = await db.chat_rooms.find_one({"id": room_id, "participantes": user["id"], "activa": True})
        if not sala:
            raise HTTPException(status_code=403, detail="Sala cerrada o sin acceso")
        now = datetime.now(timezone.utc).isoformat()
        if sala.get("expires_at", "") < now:
            await db.chat_rooms.update_one({"id": room_id}, {"$set": {"activa": False}})
            raise HTTPException(status_code=410, detail="Sala expirada")
        msg = {
            "id": str(uuid.uuid4()), "room_id": room_id,
            "usuario_id": user["id"], "usuario_nombre": user.get("nombre", ""),
            "mensaje": data.get("mensaje", "")[:500],
            "created_at": now,
        }
        await db.chat_messages.insert_one(msg)
        msg.pop("_id", None)
        return msg

    @router.post("/chat/rooms/{room_id}/close")
    async def close_chat(room_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        await db.chat_rooms.update_one({"id": room_id}, {"$set": {"activa": False}})
        return {"message": "Sala cerrada"}

    # ── App config (long-press, icons) ─────────────────────────

    @router.get("/public-config")
    async def public_config(user: dict = Depends(get_current_user)):
        tenant = await _get_tenant(user)
        config = await db.app_config.find_one({"tenant_id": tenant["id"]}, {"_id": 0}) or {}
        return {
            "long_press_ms": config.get("long_press_ms", 800),
            "sos_long_press_ms": config.get("sos_long_press_ms", 1200),
            "custom_icon": config.get("custom_icon", False),
            "tenant_nombre": tenant.get("nombre", "4Alarm"),
        }

    @router.put("/admin/long-press")
    async def set_long_press(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        update = {"tenant_id": tenant["id"]}
        if "long_press_ms" in data:
            ms = int(data["long_press_ms"])
            if ms < 300 or ms > 5000:
                raise HTTPException(status_code=400, detail="Rango: 300-5000 ms")
            update["long_press_ms"] = ms
        if "sos_long_press_ms" in data:
            ms = int(data["sos_long_press_ms"])
            if ms < 300 or ms > 5000:
                raise HTTPException(status_code=400, detail="Rango SOS: 300-5000 ms")
            update["sos_long_press_ms"] = ms
        await db.app_config.update_one({"tenant_id": tenant["id"]}, {"$set": update}, upsert=True)
        return {"message": "OK"}

    @router.post("/admin/upload-icon")
    async def upload_icon(file: UploadFile = File(...), user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        if not file.content_type.startswith("image/"):
            raise HTTPException(status_code=400, detail="Solo imágenes")
        contents = await file.read()
        if len(contents) > 2 * 1024 * 1024:
            raise HTTPException(status_code=400, detail="Máximo 2MB")
        try:
            from PIL import Image
            import io as _io
            img = Image.open(_io.BytesIO(contents))
            data_192, data_512 = None, None
            for size in (192, 512):
                resized = img.resize((size, size), Image.LANCZOS)
                buf = _io.BytesIO()
                resized.save(buf, "PNG")
                b64 = base64.b64encode(buf.getvalue()).decode()
                if size == 192:
                    data_192 = b64
                else:
                    data_512 = b64
            await db.app_config.update_one(
                {"tenant_id": tenant["id"]},
                {"$set": {"tenant_id": tenant["id"], "custom_icon": True, "icon_data_192_b64": data_192, "icon_data_512_b64": data_512, "icon_updated_at": datetime.now(timezone.utc).isoformat()}},
                upsert=True,
            )
            return {"message": "Icono actualizado"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=f"Error: {str(e)}")

    @router.get("/icon/{tenant_id}/{size}")
    async def get_icon(tenant_id: str, size: int):
        from fastapi.responses import RedirectResponse
        if size not in (192, 512):
            raise HTTPException(status_code=404, detail="Tamaño no soportado")
        config = await db.app_config.find_one({"tenant_id": tenant_id}, {"_id": 0}) or {}
        b64 = config.get("icon_data_192_b64" if size == 192 else "icon_data_512_b64")
        if not b64:
            return RedirectResponse(url="/icons/default-192.png", status_code=302)
        try:
            return Response(content=base64.b64decode(b64), media_type="image/png", headers={"Cache-Control": "public, max-age=86400"})
        except Exception:
            return RedirectResponse(url="/icons/default-192.png", status_code=302)

    # ── User profile ───────────────────────────────────────────

    @router.get("/me/profile")
    async def get_my_profile(user: dict = Depends(get_current_user)):
        doc = await db.users.find_one({"id": user["id"]}, {"_id": 0, "password_hash": 0})
        return doc or {}

    @router.put("/me/profile")
    async def update_my_profile(data: dict, user: dict = Depends(get_current_user)):
        update = {}
        nombre = (data.get("nombre") or "").strip()
        if nombre and len(nombre) >= 2:
            update["nombre"] = nombre
        if "direccion" in data:
            update["direccion"] = (data.get("direccion") or "").strip()
        if not update:
            raise HTTPException(status_code=400, detail="Nada para actualizar")
        await db.users.update_one({"id": user["id"]}, {"$set": update})
        return {"message": "Perfil actualizado", **update}

    @router.put("/admin/usuarios/{usuario_id}")
    async def admin_update_user(usuario_id: str, data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        update = {}
        if "nombre" in data:
            nombre = (data.get("nombre") or "").strip()
            if not nombre or len(nombre) < 2:
                raise HTTPException(status_code=400, detail="Nombre inválido")
            update["nombre"] = nombre
        if "direccion" in data:
            update["direccion"] = (data.get("direccion") or "").strip()
        if "email" in data:
            email = (data.get("email") or "").strip().lower()
            if email and "@" in email:
                dup = await db.users.find_one({"email": email, "id": {"$ne": usuario_id}})
                if dup:
                    raise HTTPException(status_code=400, detail="Email en uso")
                update["email"] = email
        if not update:
            raise HTTPException(status_code=400, detail="Nada para actualizar")
        result = await db.users.update_one({"id": usuario_id}, {"$set": update})
        if result.matched_count == 0:
            raise HTTPException(status_code=404, detail="No encontrado")
        return {"message": "Usuario actualizado"}

    # ── Password change ────────────────────────────────────────

    @router.put("/cambiar-password")
    async def cambiar_password(data: dict, user: dict = Depends(get_current_user)):
        import bcrypt
        current = data.get("current_password", "")
        nueva = data.get("new_password", "")
        if not current or not nueva or len(nueva) < 6:
            raise HTTPException(status_code=400, detail="Contraseña inválida o muy corta")
        user_doc = await db.users.find_one({"id": user["id"]})
        if not user_doc:
            raise HTTPException(status_code=404, detail="No encontrado")
        stored = user_doc.get("password_hash") or ""
        if not bcrypt.checkpw(current.encode(), stored.encode()):
            raise HTTPException(status_code=400, detail="Contraseña actual incorrecta")
        new_hash = bcrypt.hashpw(nueva.encode(), bcrypt.gensalt()).decode()
        await db.users.update_one({"id": user["id"]}, {"$set": {"password_hash": new_hash}})
        return {"message": "Contraseña actualizada"}

    @router.put("/admin/cambiar-password/{usuario_id}")
    async def admin_cambiar_password(usuario_id: str, data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        import bcrypt
        nueva = data.get("new_password", "")
        if not nueva or len(nueva) < 6:
            raise HTTPException(status_code=400, detail="Mínimo 6 caracteres")
        new_hash = bcrypt.hashpw(nueva.encode(), bcrypt.gensalt()).decode()
        result = await db.users.update_one({"id": usuario_id}, {"$set": {"password_hash": new_hash}})
        if result.matched_count == 0:
            raise HTTPException(status_code=404, detail="No encontrado")
        return {"message": "Contraseña actualizada"}

    # ── Registration links ─────────────────────────────────────

    @router.post("/admin/registro-links")
    async def crear_registro_link(data: dict, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        import secrets
        valid_days = int(data.get("valid_days", 7))
        if valid_days < 1 or valid_days > 365:
            raise HTTPException(status_code=400, detail="Validez 1-365 días")
        token_value = secrets.token_urlsafe(24)
        doc = {
            "id": str(uuid.uuid4()), "tenant_id": tenant["id"],
            "token": token_value, "valid_days": valid_days,
            "creado_por_id": user["id"], "creado_por_nombre": user.get("nombre", ""),
            "activo": True,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "expires_at": (datetime.now(timezone.utc) + timedelta(days=valid_days)).isoformat(),
        }
        await db.registration_links.insert_one(doc)
        doc.pop("_id", None)
        return doc

    @router.get("/admin/registro-links")
    async def listar_registro_links(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        return await db.registration_links.find({"tenant_id": tenant["id"]}, {"_id": 0}).sort("created_at", -1).to_list(100)

    @router.delete("/admin/registro-links/{link_id}")
    async def revocar_link(link_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        await db.registration_links.update_one({"id": link_id, "tenant_id": tenant["id"]}, {"$set": {"activo": False}})
        return {"message": "Revocado"}

    @router.get("/registro/{token}/validar")
    async def validar_registro_token(token: str):
        link = await db.registration_links.find_one({"token": token, "activo": True}, {"_id": 0})
        if not link:
            raise HTTPException(status_code=404, detail="Link no válido")
        now = datetime.now(timezone.utc).isoformat()
        if link.get("expires_at", "") < now:
            raise HTTPException(status_code=410, detail="Link expirado")
        tenant = await db.tenants.find_one({"id": link["tenant_id"]}, {"_id": 0, "nombre": 1, "slug": 1})
        return {"valido": True, "tenant_nombre": tenant.get("nombre", ""), "expires_at": link.get("expires_at")}

    @router.post("/registro/{token}")
    async def registrar_publico(token: str, data: dict):
        link = await db.registration_links.find_one({"token": token, "activo": True})
        if not link:
            raise HTTPException(status_code=404, detail="Link no válido")
        now = datetime.now(timezone.utc).isoformat()
        if link.get("expires_at", "") < now:
            raise HTTPException(status_code=410, detail="Link expirado")
        import bcrypt
        nombre = (data.get("nombre") or "").strip()
        direccion = (data.get("direccion") or "").strip()
        email = (data.get("email") or "").strip().lower()
        password = data.get("password", "")
        if not nombre or not email or not password:
            raise HTTPException(status_code=400, detail="Campos requeridos")
        if len(password) < 6:
            raise HTTPException(status_code=400, detail="Contraseña mínimo 6 caracteres")
        existing = await db.users.find_one({"email": email})
        if existing:
            raise HTTPException(status_code=400, detail="Email ya registrado")
        existing_p = await db.pending_registrations.find_one({"email": email, "estado": "pendiente"})
        if existing_p:
            raise HTTPException(status_code=400, detail="Solicitud pendiente con este email")
        pw_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
        pending = {
            "id": str(uuid.uuid4()), "tenant_id": link["tenant_id"],
            "nombre": nombre, "direccion": direccion, "email": email,
            "password_hash": pw_hash, "link_id": link.get("id"),
            "estado": "pendiente", "created_at": now,
        }
        await db.pending_registrations.insert_one(pending)
        return {"message": "Solicitud enviada. El administrador la revisará."}

    @router.get("/admin/usuarios-pendientes")
    async def listar_pendientes(user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        return await db.pending_registrations.find(
            {"tenant_id": tenant["id"], "estado": "pendiente"}, {"_id": 0, "password_hash": 0}
        ).sort("created_at", -1).to_list(200)

    @router.post("/admin/usuarios-pendientes/{pend_id}/aprobar")
    async def aprobar_pendiente(pend_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        tenant = await _get_tenant(user)
        p = await db.pending_registrations.find_one({"id": pend_id, "tenant_id": tenant["id"], "estado": "pendiente"})
        if not p:
            raise HTTPException(status_code=404, detail="No encontrado")
        existing = await db.users.find_one({"email": p["email"]})
        if existing:
            await db.pending_registrations.update_one({"id": pend_id}, {"$set": {"estado": "rechazado", "motivo": "Email duplicado"}})
            raise HTTPException(status_code=400, detail="Email ya existe")
        nuevo = {
            "id": str(uuid.uuid4()), "email": p["email"], "nombre": p["nombre"],
            "direccion": p.get("direccion", ""), "rol": "usuario",
            "tenant_id": tenant["id"], "password_hash": p["password_hash"],
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        await db.users.insert_one(nuevo)
        await db.pending_registrations.update_one(
            {"id": pend_id},
            {"$set": {"estado": "aprobado", "aprobado_por": user.get("nombre", ""), "aprobado_at": datetime.now(timezone.utc).isoformat()}},
        )
        return {"message": "Aprobado", "usuario_id": nuevo["id"]}

    @router.post("/admin/usuarios-pendientes/{pend_id}/rechazar")
    async def rechazar_pendiente(pend_id: str, user: dict = Depends(get_current_user)):
        _require_admin(user)
        await db.pending_registrations.update_one(
            {"id": pend_id},
            {"$set": {"estado": "rechazado", "rechazado_por": user.get("nombre", ""), "rechazado_at": datetime.now(timezone.utc).isoformat()}},
        )
        return {"message": "Rechazado"}

    return router
