"""
工作手机SDK v3.0 - 设备管理服务（含设备指纹校验）
"""

import hashlib
from typing import Optional, List, TYPE_CHECKING

try:
    from motor.motor_asyncio import AsyncIOMotorClient
except ImportError:  # NAS 无 Mongo 依赖时降级
    AsyncIOMotorClient = None  # type: ignore[misc, assignment]
import logging
from datetime import datetime

from config import settings

logger = logging.getLogger(__name__)


class DeviceManager:
    """设备管理器"""
    
    def __init__(self):
        self.client: Optional[AsyncIOMotorClient] = None
        self.db = None
    
    async def init(self):
        """初始化数据库连接（MongoDB 不可用时降级为无 DB 模式）"""
        if AsyncIOMotorClient is None:
            logger.warning("motor 未安装，MongoDB 功能降级为无 DB 模式")
            return
        try:
            self.client = AsyncIOMotorClient(
                settings.MONGO_URI,
                serverSelectionTimeoutMS=3000,  # 3秒超时，不阻塞启动
            )
            await self.client.admin.command("ping")
            self.db = self.client[settings.MONGO_DB]
            
            # 尝试创建索引
            try:
                await self.db.devices.create_index("device_id", unique=True)
                await self.db.commands.create_index("device_id")
                await self.db.commands.create_index("created_at")
                logger.info(f"MongoDB连接成功: {settings.MONGO_DB}")
            except Exception as e:
                logger.warning(f"MongoDB索引创建跳过（可能需要认证）: {e}")
                logger.info(f"MongoDB连接成功（无索引）: {settings.MONGO_DB}")
        except Exception as e:
            logger.warning(f"MongoDB 连接失败（降级为无DB模式，ADB 直连仍可用）: {e}")
            if self.client:
                self.client.close()
            self.client = None
            self.db = None
    
    async def close(self):
        """关闭连接"""
        if self.client:
            self.client.close()
    
    # ========== 设备管理 ==========
    
    async def register_device(self, device_data: dict) -> dict:
        """注册或更新设备（自动计算并校验指纹）"""
        device_id = device_data.get("device_id")

        fp_hash = self.compute_fingerprint(device_data)
        device_data["fingerprint_hash"] = fp_hash

        if self.db is None:
            return {"device_id": device_id, "updated": False, "fingerprint": fp_hash}

        device_data["updated_at"] = datetime.now()

        result = await self.db.devices.update_one(
            {"device_id": device_id},
            {
                "$set": device_data,
                "$setOnInsert": {"created_at": datetime.now()}
            },
            upsert=True
        )

        collision = await self.check_fingerprint_collision(device_id, device_data)
        if collision["collision"]:
            logger.warning(
                f"[防封] 注册设备 {device_id} 指纹碰撞: {collision['collided_with']}"
            )

        return {
            "device_id": device_id,
            "updated": result.modified_count > 0,
            "fingerprint": fp_hash,
            "fingerprint_collision": collision["collision"],
            "collided_with": collision.get("collided_with", []),
        }
    
    async def get_device(self, device_id: str) -> Optional[dict]:
        """获取设备信息"""
        if self.db is None:
            return None
        return await self.db.devices.find_one({"device_id": device_id}, {"_id": 0})
    
    async def get_all_devices(self, skip: int = 0, limit: int = 100) -> List[dict]:
        """获取所有设备"""
        if self.db is None:
            return []
        cursor = self.db.devices.find({}, {"_id": 0}).skip(skip).limit(limit)
        return await cursor.to_list(length=limit)
    
    async def update_device_status(self, device_id: str, status: str):
        """更新设备状态"""
        if self.db is None:
            return
        await self.db.devices.update_one(
            {"device_id": device_id},
            {"$set": {"status": status, "updated_at": datetime.now()}}
        )

    async def update_heartbeat(self, device_id: str):
        """更新设备心跳时间（设备状态实时反馈到服务器/数据库）"""
        if self.db is None:
            return
        now = datetime.now()
        await self.db.devices.update_one(
            {"device_id": device_id},
            {"$set": {"last_heartbeat": now, "status": "online", "updated_at": now}},
            upsert=True
        )
    
    # ========== 设备指纹校验（防封 AF11） ==========

    @staticmethod
    def compute_fingerprint(info: dict) -> str:
        """从设备上报信息计算指纹哈希（MD5）。

        防封要点（G2 修复 2026-05-31）：硬件字段缺失时，绝不能让所有设备
        都得到空串 MD5（d41d8cd98f00b204e9800998ecf8427e）—— 那会令全部设备
        互相"指纹碰撞"，使碰撞检测失效。无硬件数据时回退到 device_id 作为
        稳定种子，保证一机一指纹；同时兼容嵌套 display.{width,height}。
        """
        keys = sorted([
            "brand", "model", "manufacturer", "android_version",
            "sdk_version", "serial", "imei", "mac", "bluetooth_mac",
            "screen_width", "screen_height", "density",
            "cpu_abi", "fingerprint", "android_id",
        ])
        merged = dict(info or {})
        # 兼容 agent 上报的嵌套 display.{width,height}
        disp = merged.get("display")
        if isinstance(disp, dict):
            merged.setdefault("screen_width", disp.get("width") or disp.get("displayWidth"))
            merged.setdefault("screen_height", disp.get("height") or disp.get("displayHeight"))
        parts = []
        for k in keys:
            v = merged.get(k, "")
            if v:
                parts.append(f"{k}={v}")
        if not parts:
            # 无任何硬件字段：用 device_id 兜底，避免全设备空哈希误撞
            did = merged.get("device_id", "")
            if did:
                parts.append(f"device_id={did}")
        raw = "|".join(parts)
        return hashlib.md5(raw.encode("utf-8")).hexdigest()

    async def check_fingerprint_collision(self, device_id: str, info: dict) -> dict:
        """
        检查设备指纹是否与其他设备碰撞。
        返回 {"collision": bool, "collided_with": [device_ids...]}
        """
        fp = self.compute_fingerprint(info)

        if self.db is None:
            return {"collision": False, "collided_with": [], "fingerprint": fp}

        try:
            await self.db.devices.update_one(
                {"device_id": device_id},
                {"$set": {"fingerprint_hash": fp, "fingerprint_info": info}},
                upsert=True,
            )
        except Exception as e:
            logger.warning(f"指纹写入失败: {e}")

        try:
            cursor = self.db.devices.find(
                {"fingerprint_hash": fp, "device_id": {"$ne": device_id}},
                {"device_id": 1, "_id": 0},
            )
            collisions = [doc["device_id"] async for doc in cursor]
        except Exception as e:
            logger.warning(f"指纹碰撞查询失败: {e}")
            collisions = []

        if collisions:
            logger.warning(
                f"[防封] 设备指纹碰撞! {device_id} 与 {collisions} 指纹相同 (hash={fp})"
            )

        return {
            "collision": len(collisions) > 0,
            "collided_with": collisions,
            "fingerprint": fp,
        }

    # ========== 命令日志 ==========
    
    async def log_command(
        self,
        device_id: str,
        command_type: str,
        params: dict,
        result: dict
    ):
        """记录命令执行日志"""
        if self.db is None:
            return
        try:
            await self.db.commands.insert_one({
                "device_id": device_id,
                "command_type": command_type,
                "params": params,
                "result": result,
                "created_at": datetime.now()
            })
        except Exception as e:
            # Mongo 无权限/不可用时不阻断主流程
            logger.warning(f"命令日志写入失败（已跳过）: {e}")
    
    async def get_command_logs(
        self,
        device_id: str,
        limit: int = 50
    ) -> List[dict]:
        """获取命令日志"""
        if self.db is None:
            return []
        try:
            cursor = self.db.commands.find(
                {"device_id": device_id},
                {"_id": 0}
            ).sort("created_at", -1).limit(limit)
            return await cursor.to_list(length=limit)
        except Exception as e:
            logger.warning(f"命令日志查询失败（已跳过）: {e}")
            return []


# 全局实例
device_manager = DeviceManager()
