import json
from datetime import timedelta
from typing import List
from fastapi import WebSocket
from sqlalchemy.orm import Session
from app.models.device import Device
from app.models.command import FleetCommand
from app.models.telemetry import TryOnSession
from app.config import settings
from app.utils.timezone import jakarta_now

class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        if websocket in self.active_connections:
            self.active_connections.remove(websocket)

    async def broadcast_device_update(self, device_data: dict):
        message = json.dumps({"type": "DEVICE_UPDATE", "data": device_data})
        for connection in self.active_connections:
            try:
                await connection.send_text(message)
            except Exception:
                pass

    async def broadcast_fleet_stats(self, stats: dict):
        message = json.dumps({"type": "FLEET_STATS", "data": stats})
        for connection in self.active_connections:
            try:
                await connection.send_text(message)
            except Exception:
                pass

manager = ConnectionManager()

class FleetService:
    @staticmethod
    def update_heartbeat(
        db: Session,
        device_id: str,
        device_name: str = None,
        device_model: str = None,
        android_version: str = None,
        app_version: str = None,
        ip_address: str = None,
        battery_level: int = 100,
        is_charging: bool = True,
        fps: float = 30.0,
        latency_ms: int = 45,
        current_cloth_id: int = None,
        current_cloth_sku: str = "",
        current_cloth_name: str = "None",
        status: str = "ONLINE",
        is_kiosk_locked: bool = False
    ) -> Device:
        device = db.query(Device).filter(Device.device_id == device_id).first()
        now = jakarta_now()

        if not device:
            device = Device(
                device_id=device_id,
                device_name=device_name or f"Kiosk-{device_id[:6]}",
                device_model=device_model or "Android Kiosk",
                android_version=android_version or "14",
                app_version=app_version or "1.0.0",
                ip_address=ip_address or "127.0.0.1",
                battery_level=battery_level,
                is_charging=is_charging,
                fps=fps,
                latency_ms=latency_ms,
                current_cloth_id=current_cloth_id,
                current_cloth_sku=current_cloth_sku,
                current_cloth_name=current_cloth_name,
                status=status,
                is_kiosk_locked=is_kiosk_locked,
                last_heartbeat=now,
                created_at=now
            )
            db.add(device)
        else:
            if device_name:
                device.device_name = device_name
            if device_model:
                device.device_model = device_model
            if android_version:
                device.android_version = android_version
            if app_version:
                device.app_version = app_version
            if ip_address:
                device.ip_address = ip_address
            device.battery_level = battery_level
            device.is_charging = is_charging
            device.fps = fps
            device.latency_ms = latency_ms
            device.current_cloth_id = current_cloth_id
            device.current_cloth_sku = current_cloth_sku
            device.current_cloth_name = current_cloth_name
            device.status = status
            device.is_kiosk_locked = is_kiosk_locked
            device.last_heartbeat = now

        db.commit()
        db.refresh(device)
        return device

    @staticmethod
    def get_all_devices(db: Session) -> List[Device]:
        cutoff = jakarta_now() - timedelta(seconds=settings.HEARTBEAT_TIMEOUT_SECONDS)
        devices = db.query(Device).all()
        for d in devices:
            if d.last_heartbeat:
                # Handle timezone comparison safely
                hb = d.last_heartbeat
                if hb.tzinfo is None:
                    from app.utils.timezone import JAKARTA_TZ
                    hb = hb.replace(tzinfo=JAKARTA_TZ)
                if hb < cutoff and d.status != "OFFLINE":
                    d.status = "OFFLINE"
        db.commit()
        return devices

    @staticmethod
    def queue_command(db: Session, device_id: str, command_type: str, payload: dict) -> FleetCommand:
        cmd = FleetCommand(
            device_id=device_id,
            command_type=command_type,
            payload=json.dumps(payload),
            status="PENDING",
            created_at=jakarta_now()
        )
        db.add(cmd)
        db.commit()
        db.refresh(cmd)
        return cmd

    @staticmethod
    def get_pending_commands(db: Session, device_id: str) -> List[FleetCommand]:
        return db.query(FleetCommand).filter(
            FleetCommand.device_id == device_id,
            FleetCommand.status == "PENDING"
        ).all()

    @staticmethod
    def acknowledge_command(db: Session, command_id: int) -> bool:
        cmd = db.query(FleetCommand).filter(FleetCommand.id == command_id).first()
        if cmd:
            cmd.status = "ACKNOWLEDGED"
            cmd.executed_at = jakarta_now()
            db.commit()
            return True
        return False

    @staticmethod
    def get_fleet_stats(db: Session) -> dict:
        cutoff = jakarta_now() - timedelta(seconds=settings.HEARTBEAT_TIMEOUT_SECONDS)
        total_devices = db.query(Device).count()
        devices = db.query(Device).all()
        
        online_devices = 0
        active_tryons = 0
        from app.utils.timezone import JAKARTA_TZ
        for d in devices:
            if d.last_heartbeat:
                hb = d.last_heartbeat if d.last_heartbeat.tzinfo else d.last_heartbeat.replace(tzinfo=JAKARTA_TZ)
                if hb >= cutoff:
                    online_devices += 1
                    if d.status == "TRYING_ON":
                        active_tryons += 1

        total_sessions = db.query(TryOnSession).count()
        
        return {
            "total_devices": total_devices,
            "online_devices": online_devices,
            "offline_devices": max(0, total_devices - online_devices),
            "active_tryon_sessions": active_tryons,
            "total_lifetime_sessions": total_sessions,
            "system_health": "OPTIMAL" if online_devices > 0 or total_devices == 0 else "DEGRADED"
        }
