# -*- coding: utf-8 -*-
"""
SDK客户端 - 负责认证和设备管理
"""

import requests
import logging
from typing import List, Optional, Dict

from .device import Device
from .exceptions import AuthError, DeviceNotFoundError, ConnectionError
from .constants import DEFAULT_PORT, DEFAULT_TIMEOUT

logger = logging.getLogger("qunkong")


class Client:
    """
    蜂巢云控客户端
    
    支持三种模式：
    1. debug模式：电脑直连手机（局域网）
    2. 中继模式：通过用户自建的中继服务器
    3. 中央模式：通过中央服务器（暂不支持命令转发）
    
    Example:
        >>> # 调试模式 - 电脑直连手机
        >>> client = Client(api_key="sk-xxx", debug=True)
        >>> device = client.connect_direct("192.168.1.100")
        
        >>> # 生产模式 - 通过中继服务器
        >>> client = Client(
        ...     api_key="sk-xxx",
        ...     relay_server="http://your-server:9999"
        ... )
        >>> device = client.get_device("DEVICE_001")
    """
    
    def __init__(
        self,
        api_key: str,
        server: str = "https://qkh.lhy.lat",
        relay_server: str = None,
        debug: bool = False,
        debug_ip: str = None,
        debug_port: int = DEFAULT_PORT,
        timeout: int = DEFAULT_TIMEOUT,
        auto_connect: bool = True
    ):
        """
        初始化客户端
        
        Args:
            api_key: API密钥
            server: 中央服务器地址（用于认证和设备管理）
            relay_server: 中继服务器地址（用户自建，接收SDK命令）
            debug: 调试模式，直连手机
            debug_ip: 调试模式下的手机IP
            debug_port: 调试模式下的手机端口
            timeout: 请求超时时间
            auto_connect: 是否自动连接
        """
        self.api_key = api_key
        self.server = server.rstrip("/")
        self.relay_server = relay_server.rstrip("/") if relay_server else None
        self.debug = debug
        self.debug_ip = debug_ip
        self.debug_port = debug_port
        self.timeout = timeout
        self.auto_connect = auto_connect
        self._devices_cache: Dict[str, Device] = {}
        self._device_list: List[dict] = []
        
        # 确定工作模式
        if debug:
            self._mode = "debug"
            logger.info("SDK模式: 调试模式（直连手机）")
        elif relay_server:
            self._mode = "relay"
            logger.info(f"SDK模式: 中继模式 ({relay_server})")
        else:
            self._mode = "central"
            logger.info("SDK模式: 中央模式")
        
        # 验证API Key
        if auto_connect and not debug:
            self._verify_api_key()
    
    def _verify_api_key(self):
        """验证API Key有效性"""
        try:
            response = requests.post(
                f"{self.server}/api/v1/sdk/verify",
                headers={"Authorization": f"Bearer {self.api_key}"},
                timeout=self.timeout
            )
            
            if response.status_code == 401:
                raise AuthError("API Key无效")
            elif response.status_code != 200:
                logger.warning(f"API Key验证返回: {response.status_code}")
                
        except requests.exceptions.RequestException as e:
            logger.warning(f"无法连接服务器验证API Key: {e}")
            # 离线模式，不阻止使用
    
    def _fetch_device_list(self) -> List[dict]:
        """从服务器获取设备列表"""
        # 中继模式：从中继服务器获取
        if self._mode == "relay" and self.relay_server:
            return self._fetch_from_relay()
        
        # 中央模式：从中央服务器获取
        try:
            response = requests.get(
                f"{self.server}/api/v1/sdk/devices",
                headers={"Authorization": f"Bearer {self.api_key}"},
                timeout=self.timeout
            )
            
            if response.status_code == 200:
                data = response.json()
                self._device_list = data.get("devices", [])
                return self._device_list
            else:
                logger.warning(f"获取设备列表失败: {response.status_code}")
                return []
                
        except requests.exceptions.RequestException as e:
            logger.warning(f"获取设备列表失败: {e}")
            return []
    
    def _fetch_from_relay(self) -> List[dict]:
        """从中继服务器获取设备列表"""
        try:
            response = requests.get(
                f"{self.relay_server}/api/devices",
                headers={"Authorization": f"Bearer {self.api_key}"},
                timeout=self.timeout
            )
            
            if response.status_code == 200:
                data = response.json()
                devices = data.get("devices", [])
                self._device_list = devices
                return devices
            else:
                logger.warning(f"获取中继设备列表失败: {response.status_code}")
                return []
                
        except requests.exceptions.RequestException as e:
            logger.warning(f"获取中继设备列表失败: {e}")
            return []
    
    def get_device(
        self,
        device_id: str = None,
        ip: str = None,
        port: int = DEFAULT_PORT
    ) -> Device:
        """
        获取设备对象
        
        根据模式不同，创建不同类型的连接：
        - debug模式：直连手机IP:8888
        - relay模式：通过中继服务器API
        
        Args:
            device_id: 设备ID
            ip: 设备IP地址（仅debug模式）
            port: 设备端口（仅debug模式）
        
        Returns:
            Device对象
        """
        # 调试模式：直连手机
        if self._mode == "debug":
            target_ip = ip or self.debug_ip
            target_port = port if ip else self.debug_port
            
            if not target_ip:
                raise ValueError("调试模式需要指定手机IP（通过ip参数或debug_ip）")
            
            cache_key = f"debug:{target_ip}:{target_port}"
            if cache_key not in self._devices_cache:
                self._devices_cache[cache_key] = Device(
                    ip=target_ip,
                    port=target_port,
                    device_id=device_id or f"{target_ip}:{target_port}",
                    api_key=self.api_key,
                    mode="direct"  # 直连模式
                )
            return self._devices_cache[cache_key]
        
        # 中继模式：通过中继服务器
        if self._mode == "relay":
            if not device_id:
                raise ValueError("中继模式需要指定device_id")
            
            if device_id in self._devices_cache:
                return self._devices_cache[device_id]
            
            # 检查设备是否在线
            if not self._check_device_online_relay(device_id):
                raise DeviceNotFoundError(f"设备 {device_id} 不在线")
            
            device = Device(
                device_id=device_id,
                api_key=self.api_key,
                relay_server=self.relay_server,
                mode="relay"  # 中继模式
            )
            self._devices_cache[device_id] = device
            return device
        
        # 中央模式（直连或获取IP）
        if ip:
            cache_key = f"{ip}:{port}"
            if cache_key not in self._devices_cache:
                self._devices_cache[cache_key] = Device(
                    ip=ip,
                    port=port,
                    api_key=self.api_key,
                    mode="direct"
                )
            return self._devices_cache[cache_key]
        
        if device_id:
            if device_id in self._devices_cache:
                return self._devices_cache[device_id]
            
            device_info = self._get_device_info(device_id)
            if device_info and device_info.get("ip"):
                device = Device(
                    device_id=device_id,
                    ip=device_info.get("ip"),
                    port=device_info.get("port", DEFAULT_PORT),
                    api_key=self.api_key,
                    info=device_info,
                    mode="direct"
                )
                self._devices_cache[device_id] = device
                return device
            else:
                raise DeviceNotFoundError(f"设备 {device_id} 未找到或不在线")
        
        raise ValueError("必须提供 device_id 或 ip 参数")
    
    def _check_device_online_relay(self, device_id: str) -> bool:
        """检查设备是否在中继服务器上在线"""
        try:
            response = requests.get(
                f"{self.relay_server}/api/device/{device_id}/online",
                timeout=self.timeout
            )
            if response.status_code == 200:
                return response.json().get("online", False)
            return False
        except:
            return False
    
    def _get_device_info(self, device_id: str) -> Optional[dict]:
        """获取单个设备信息"""
        try:
            response = requests.get(
                f"{self.server}/api/v1/sdk/device/{device_id}",
                headers={"Authorization": f"Bearer {self.api_key}"},
                timeout=self.timeout
            )
            
            if response.status_code == 200:
                return response.json()
            return None
            
        except requests.exceptions.RequestException:
            return None
    
    def get_online_devices(self) -> List[Device]:
        """
        获取所有在线设备
        
        Returns:
            在线设备列表
        
        Example:
            >>> devices = client.get_online_devices()
            >>> for device in devices:
            ...     device.click(500, 800)
        """
        self._fetch_device_list()
        
        devices = []
        for info in self._device_list:
            if info.get("status") == "online" and info.get("ip"):
                device = Device(
                    device_id=info.get("device_id"),
                    ip=info.get("ip"),
                    port=info.get("port", DEFAULT_PORT),
                    api_key=self.api_key,
                    info=info
                )
                devices.append(device)
                self._devices_cache[info.get("device_id")] = device
        
        return devices
    
    def get_all_devices(self) -> List[Device]:
        """
        获取所有设备（包括离线）
        
        Returns:
            所有设备列表
        """
        self._fetch_device_list()
        
        devices = []
        for info in self._device_list:
            device = Device(
                device_id=info.get("device_id"),
                ip=info.get("ip"),
                port=info.get("port", DEFAULT_PORT),
                api_key=self.api_key,
                info=info
            )
            devices.append(device)
            self._devices_cache[info.get("device_id")] = device
        
        return devices
    
    def scan_lan(self, port: int = DEFAULT_PORT, timeout: float = 0.5) -> List[Device]:
        """
        扫描局域网发现设备
        
        Args:
            port: 扫描端口
            timeout: 单个IP超时时间
        
        Returns:
            发现的设备列表
        """
        import socket
        import concurrent.futures
        
        # 获取本机IP
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        try:
            s.connect(("8.8.8.8", 80))
            local_ip = s.getsockname()[0]
        finally:
            s.close()
        
        # 计算局域网范围
        ip_prefix = ".".join(local_ip.split(".")[:-1])
        
        def check_ip(ip):
            try:
                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                sock.settimeout(timeout)
                result = sock.connect_ex((ip, port))
                sock.close()
                if result == 0:
                    return ip
            except:
                pass
            return None
        
        devices = []
        ips_to_scan = [f"{ip_prefix}.{i}" for i in range(1, 255)]
        
        with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
            results = executor.map(check_ip, ips_to_scan)
            for ip in results:
                if ip:
                    device = Device(ip=ip, port=port, api_key=self.api_key)
                    devices.append(device)
                    logger.info(f"发现设备: {ip}:{port}")
        
        return devices
    
    def connect_direct(self, ip: str, port: int = DEFAULT_PORT) -> Device:
        """
        直接连接设备（局域网模式）
        
        Args:
            ip: 设备IP
            port: 设备端口
        
        Returns:
            Device对象
        
        Example:
            >>> device = client.connect_direct("192.168.1.100")
            >>> device.click(500, 800)
        """
        return self.get_device(ip=ip, port=port)
