""" Superadmin endpoints for device management. """ from datetime import datetime, timezone from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.ext.asyncio import AsyncSession from app.api.deps import get_current_superadmin from app.core.database import get_db from app.models.user import User from app.schemas.device import ( DeviceCreate, DeviceListResponse, DeviceResponse, DeviceUpdate, ) from app.services import device_service router = APIRouter() @router.get("", response_model=DeviceListResponse) async def list_devices( db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(get_current_superadmin)], skip: int = Query(0, ge=0, description="Number of records to skip"), limit: int = Query(100, ge=1, le=1000, description="Max records to return"), organization_id: int | None = Query( None, description="Filter by organization" ), status: str | None = Query(None, description="Filter by status"), search: str | None = Query( None, min_length=2, description="Universal search: MAC, simple_id, organization name/email", ), ): """ List all devices (superadmin only). Returns paginated list of devices with optional filters. Search parameter searches across: - MAC address - Simple ID (#1, #2, etc) - Organization name - Organization contact email """ devices, total = await device_service.list_devices( db, skip=skip, limit=limit, organization_id=organization_id, status=status, search=search, ) # Update status dynamically based on last_seen_at # Device is online if it fetched config within last 60 seconds now = datetime.now(timezone.utc) for device in devices: if device.last_seen_at: # Remove timezone info for comparison if needed last_seen = device.last_seen_at if last_seen.tzinfo is None: last_seen = last_seen.replace(tzinfo=timezone.utc) delta_seconds = (now - last_seen).total_seconds() device.status = "online" if delta_seconds < 60 else "offline" else: device.status = "offline" return DeviceListResponse( devices=devices, total=total, ) @router.get("/{device_id}", response_model=DeviceResponse) async def get_device( device_id: int, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(get_current_superadmin)], ): """ Get device by ID (superadmin only). """ device = await device_service.get_device(db, device_id) if not device: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Device not found", ) return device @router.post( "", response_model=DeviceResponse, status_code=status.HTTP_201_CREATED ) async def create_device( data: DeviceCreate, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(get_current_superadmin)], ): """ Register a new device (superadmin only). Devices are assigned a unique simple_id (Receiver #1, #2, etc). """ try: device = await device_service.create_device(db, data) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e), ) return device @router.patch("/{device_id}", response_model=DeviceResponse) async def update_device( device_id: int, data: DeviceUpdate, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(get_current_superadmin)], ): """ Update device (superadmin only). Can update device organization assignment, status, and configuration. """ device = await device_service.update_device(db, device_id, data) if not device: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Device not found", ) return device @router.delete("/{device_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_device( device_id: int, db: Annotated[AsyncSession, Depends(get_db)], current_user: Annotated[User, Depends(get_current_superadmin)], ): """ Delete device (superadmin only). Warning: This permanently deletes the device. """ deleted = await device_service.delete_device(db, device_id) if not deleted: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Device not found", )