import json
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException, WebSocket, WebSocketDisconnect
from sqlalchemy.orm import Session
from pydantic import BaseModel
from app.database import get_db
from app.services.fleet_service import FleetService, manager
from app.models.device import Device
from app.models.telemetry import TryOnSession
from app.models.command import FleetCommand

router = APIRouter()

class HeartbeatRequest(BaseModel):
    device_id: str
    device_name: Optional[str] = None
    device_model: Optional[str] = None
    android_version: Optional[str] = None
    app_version: Optional[str] = None
    ip_address: Optional[str] = None
    battery_level: int = 100
    is_charging: bool = True
    fps: float = 30.0
    latency_ms: int = 45
    current_cloth_id: Optional[int] = None
    current_cloth_sku: Optional[str] = ""
    current_cloth_name: Optional[str] = "None"
    status: str = "ONLINE"
    is_kiosk_locked: bool = False

class CommandRequest(BaseModel):
    command_type: str  # SELECT_CLOTH, REFRESH_CATALOG, RESTART_STREAM, SET_PROMPT, LOCK_KIOSK, UNLOCK_KIOSK, PING
    payload: Optional[dict] = {}

class SessionLogRequest(BaseModel):
    device_id: str
    cloth_id: Optional[int] = None
    cloth_name: Optional[str] = ""
    duration_seconds: int = 0
    avg_fps: float = 30.0

@router.post("/heartbeat")
async def receive_heartbeat(payload: HeartbeatRequest, db: Session = Depends(get_db)):
    device = FleetService.update_heartbeat(
        db=db,
        device_id=payload.device_id,
        device_name=payload.device_name,
        device_model=payload.device_model,
        android_version=payload.android_version,
        app_version=payload.app_version,
        ip_address=payload.ip_address,
        battery_level=payload.battery_level,
        is_charging=payload.is_charging,
        fps=payload.fps,
        latency_ms=payload.latency_ms,
        current_cloth_id=payload.current_cloth_id,
        current_cloth_sku=payload.current_cloth_sku,
        current_cloth_name=payload.current_cloth_name,
        status=payload.status,
        is_kiosk_locked=payload.is_kiosk_locked
    )
    
    # Broadcast to web dashboard via WebSocket
    await manager.broadcast_device_update(device.to_dict())
    
    # Check pending commands for this device
    pending_commands = FleetService.get_pending_commands(db, payload.device_id)
    commands_data = [cmd.to_dict() for cmd in pending_commands]

    return {
        "status": "success",
        "device_id": device.device_id,
        "is_kiosk_locked": device.is_kiosk_locked,
        "pending_commands": commands_data
    }

@router.get("/devices")
def list_devices(db: Session = Depends(get_db)):
    devices = FleetService.get_all_devices(db)
    return [d.to_dict() for d in devices]

@router.get("/devices/{device_id}")
def get_device(device_id: str, db: Session = Depends(get_db)):
    device = db.query(Device).filter(Device.device_id == device_id).first()
    if not device:
        raise HTTPException(status_code=404, detail="Device not found")
    return device.to_dict()

@router.delete("/devices/{device_id}")
def delete_device(device_id: str, db: Session = Depends(get_db)):
    device = db.query(Device).filter(Device.device_id == device_id).first()
    if not device:
        raise HTTPException(status_code=404, detail="Device not found")
    
    # Delete related commands
    db.query(FleetCommand).filter(FleetCommand.device_id == device_id).delete()
    db.delete(device)
    db.commit()
    return {"status": "deleted", "device_id": device_id}

@router.delete("/devices")
def clear_all_devices(db: Session = Depends(get_db)):
    deleted_devices = db.query(Device).delete()
    db.query(FleetCommand).delete()
    db.commit()
    return {"status": "cleared", "deleted_count": deleted_devices}

@router.post("/devices/{device_id}/command")
def send_command(device_id: str, cmd_req: CommandRequest, db: Session = Depends(get_db)):
    command = FleetService.queue_command(
        db=db,
        device_id=device_id,
        command_type=cmd_req.command_type,
        payload=cmd_req.payload or {}
    )
    return {"status": "queued", "command": command.to_dict()}

@router.get("/devices/{device_id}/commands/pending")
def get_pending_commands(device_id: str, db: Session = Depends(get_db)):
    cmds = FleetService.get_pending_commands(db, device_id)
    return [c.to_dict() for c in cmds]

@router.post("/commands/{command_id}/ack")
def acknowledge_command(command_id: int, db: Session = Depends(get_db)):
    success = FleetService.acknowledge_command(db, command_id)
    if not success:
        raise HTTPException(status_code=404, detail="Command not found")
    return {"status": "acknowledged"}

@router.post("/session-log")
def log_tryon_session(log: SessionLogRequest, db: Session = Depends(get_db)):
    session = TryOnSession(
        device_id=log.device_id,
        cloth_id=log.cloth_id,
        cloth_name=log.cloth_name or "Unknown",
        duration_seconds=log.duration_seconds,
        avg_fps=log.avg_fps
    )
    db.add(session)
    db.commit()
    db.refresh(session)
    return {"status": "logged", "session": session.to_dict()}

@router.get("/stats")
def get_stats(db: Session = Depends(get_db)):
    return FleetService.get_fleet_stats(db)

@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            data = await websocket.receive_text()
            if data == "ping":
                await websocket.send_text(json.dumps({"type": "PONG"}))
    except WebSocketDisconnect:
        manager.disconnect(websocket)
    except Exception:
        manager.disconnect(websocket)
